OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chromecast.shell; |
| 6 |
| 7 import android.app.Activity; |
| 8 import android.content.BroadcastReceiver; |
| 9 import android.content.Context; |
| 10 import android.content.Intent; |
| 11 import android.content.IntentFilter; |
| 12 import android.media.AudioManager; |
| 13 import android.net.Uri; |
| 14 import android.os.Bundle; |
| 15 import android.support.v4.content.LocalBroadcastManager; |
| 16 import android.util.Log; |
| 17 import android.view.KeyEvent; |
| 18 import android.view.MotionEvent; |
| 19 import android.view.WindowManager; |
| 20 import android.widget.Toast; |
| 21 |
| 22 import org.chromium.base.CommandLine; |
| 23 import org.chromium.content.browser.ActivityContentVideoViewClient; |
| 24 import org.chromium.content.browser.ContentVideoViewClient; |
| 25 import org.chromium.content.browser.ContentViewClient; |
| 26 import org.chromium.content.browser.ContentViewCore; |
| 27 import org.chromium.ui.base.WindowAndroid; |
| 28 |
| 29 /** |
| 30 * Activity for managing the Cast shell. |
| 31 */ |
| 32 public class CastShellActivity extends Activity { |
| 33 private static final String TAG = "CastShellActivity"; |
| 34 |
| 35 private static final String ACTIVE_SHELL_URL_KEY = "activeUrl"; |
| 36 private static final int DEFAULT_HEIGHT_PIXELS = 720; |
| 37 public static final String ACTION_EXTRA_RESOLUTION_HEIGHT = |
| 38 "org.chromium.chromecast.shell.intent.extra.RESOLUTION_HEIGHT"; |
| 39 |
| 40 private CastWindowManager mCastWindowManager; |
| 41 private AudioManager mAudioManager; |
| 42 private BroadcastReceiver mBroadcastReceiver; |
| 43 |
| 44 // Native window instance. |
| 45 // TODO(byungchul, gunsch): CastShellActivity, CastWindowAndroid, and native
CastWindowAndroid |
| 46 // have a one-to-one relationship. Consider instantiating CastWindow here an
d CastWindow having |
| 47 // this native shell instance. |
| 48 private long mNativeCastWindow; |
| 49 |
| 50 /** |
| 51 * Returns whether or not CastShellActivity should launch the browser startu
p sequence. |
| 52 * Intended to be overridden. |
| 53 */ |
| 54 protected boolean shouldLaunchBrowser() { |
| 55 return true; |
| 56 } |
| 57 |
| 58 @Override |
| 59 protected void onCreate(final Bundle savedInstanceState) { |
| 60 super.onCreate(savedInstanceState); |
| 61 exitIfUrlMissing(); |
| 62 |
| 63 if (shouldLaunchBrowser()) { |
| 64 if (!CastBrowserHelper.initializeBrowser(getApplicationContext())) { |
| 65 Toast.makeText(this, |
| 66 R.string.browser_process_initialization_failed, |
| 67 Toast.LENGTH_SHORT).show(); |
| 68 finish(); |
| 69 } |
| 70 } |
| 71 |
| 72 // Whenever our app is visible, volume controls should modify the music
stream. |
| 73 // For more information read: |
| 74 // http://developer.android.com/training/managing-audio/volume-playback.
html |
| 75 setVolumeControlStream(AudioManager.STREAM_MUSIC); |
| 76 |
| 77 // Set flags to both exit sleep mode when this activity starts and |
| 78 // avoid entering sleep mode while playing media. We cannot distinguish |
| 79 // between video and audio so this applies to both. |
| 80 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); |
| 81 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
| 82 |
| 83 mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE); |
| 84 |
| 85 setContentView(R.layout.cast_shell_activity); |
| 86 mCastWindowManager = (CastWindowManager) findViewById(R.id.shell_contain
er); |
| 87 mCastWindowManager.setDelegate(new CastWindowManager.Delegate() { |
| 88 @Override |
| 89 public void onCreated() { |
| 90 } |
| 91 |
| 92 @Override |
| 93 public void onClosed() { |
| 94 mNativeCastWindow = 0; |
| 95 mCastWindowManager.setDelegate(null); |
| 96 finish(); |
| 97 } |
| 98 }); |
| 99 setResolution(); |
| 100 mCastWindowManager.setWindow(new WindowAndroid(this)); |
| 101 |
| 102 registerBroadcastReceiver(); |
| 103 |
| 104 String url = getIntent().getDataString(); |
| 105 Log.d(TAG, "onCreate startupUrl: " + url); |
| 106 mNativeCastWindow = mCastWindowManager.launchCastWindow(url); |
| 107 |
| 108 getActiveContentViewCore().setContentViewClient(new ContentViewClient()
{ |
| 109 @Override |
| 110 public ContentVideoViewClient getContentVideoViewClient() { |
| 111 return new ActivityContentVideoViewClient(CastShellActivity.this
); |
| 112 } |
| 113 }); |
| 114 } |
| 115 |
| 116 @Override |
| 117 protected void onDestroy() { |
| 118 super.onDestroy(); |
| 119 |
| 120 unregisterBroadcastReceiver(); |
| 121 |
| 122 if (mNativeCastWindow != 0) { |
| 123 mCastWindowManager.stopCastWindow(mNativeCastWindow); |
| 124 mNativeCastWindow = 0; |
| 125 } |
| 126 } |
| 127 |
| 128 @Override |
| 129 protected void onNewIntent(Intent intent) { |
| 130 // Only handle direct intents (e.g. "fling") if this activity is also ma
naging |
| 131 // the browser process. |
| 132 if (!shouldLaunchBrowser()) return; |
| 133 |
| 134 String url = intent.getDataString(); |
| 135 Log.d(TAG, "onNewIntent: " + url); |
| 136 |
| 137 // Reset broadcast intent uri and receiver. |
| 138 setIntent(intent); |
| 139 exitIfUrlMissing(); |
| 140 getActiveCastWindow().loadUrl(url); |
| 141 } |
| 142 |
| 143 @Override |
| 144 protected void onResume() { |
| 145 super.onResume(); |
| 146 |
| 147 // Inform ContentView that this activity is being shown. |
| 148 ContentViewCore view = getActiveContentViewCore(); |
| 149 if (view != null) view.onShow(); |
| 150 |
| 151 // Request audio focus so any other audio playback doesn't continue in t
he background. |
| 152 if (mAudioManager.requestAudioFocus( |
| 153 null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) |
| 154 != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |
| 155 Log.e(TAG, "Failed to obtain audio focus"); |
| 156 } |
| 157 } |
| 158 |
| 159 @Override |
| 160 protected void onPause() { |
| 161 // As soon as the cast app is no longer in the foreground, we ought to i
mmediately tear |
| 162 // everything down. Apps should not continue running and playing sound i
n the background. |
| 163 super.onPause(); |
| 164 |
| 165 // Release the audio focus. Note that releasing audio focus does not sto
p audio playback, |
| 166 // it just notifies the framework that this activity has stopped playing
audio. |
| 167 if (mAudioManager.abandonAudioFocus(null) != AudioManager.AUDIOFOCUS_REQ
UEST_GRANTED) { |
| 168 Log.e(TAG, "Failed to abandon audio focus"); |
| 169 } |
| 170 |
| 171 ContentViewCore view = getActiveContentViewCore(); |
| 172 if (view != null) view.onHide(); |
| 173 |
| 174 finishGracefully(); |
| 175 } |
| 176 |
| 177 protected void finishGracefully() { |
| 178 if (mNativeCastWindow != 0) { |
| 179 mCastWindowManager.stopCastWindow(mNativeCastWindow); |
| 180 mNativeCastWindow = 0; |
| 181 } |
| 182 } |
| 183 |
| 184 private void registerBroadcastReceiver() { |
| 185 if (mBroadcastReceiver == null) { |
| 186 mBroadcastReceiver = new BroadcastReceiver() { |
| 187 @Override |
| 188 public void onReceive(Context context, Intent intent) { |
| 189 Log.d(TAG, "Received intent: action=" + intent.getAction()); |
| 190 if (CastWindowAndroid.ACTION_ENABLE_DEV_TOOLS.equals(intent.
getAction())) { |
| 191 mCastWindowManager.nativeEnableDevTools(true); |
| 192 } else if (CastWindowAndroid.ACTION_DISABLE_DEV_TOOLS.equals
( |
| 193 intent.getAction())) { |
| 194 mCastWindowManager.nativeEnableDevTools(false); |
| 195 } |
| 196 } |
| 197 }; |
| 198 } |
| 199 |
| 200 IntentFilter devtoolsBroadcastIntentFilter = new IntentFilter(); |
| 201 devtoolsBroadcastIntentFilter.addAction(CastWindowAndroid.ACTION_ENABLE_
DEV_TOOLS); |
| 202 devtoolsBroadcastIntentFilter.addAction(CastWindowAndroid.ACTION_DISABLE
_DEV_TOOLS); |
| 203 LocalBroadcastManager.getInstance(this) |
| 204 .registerReceiver(mBroadcastReceiver, devtoolsBroadcastIntentFil
ter); |
| 205 } |
| 206 |
| 207 private void unregisterBroadcastReceiver() { |
| 208 LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstan
ce(this); |
| 209 broadcastManager.unregisterReceiver(mBroadcastReceiver); |
| 210 } |
| 211 |
| 212 private void setResolution() { |
| 213 int requestedHeight = getIntent().getIntExtra( |
| 214 ACTION_EXTRA_RESOLUTION_HEIGHT, DEFAULT_HEIGHT_PIXELS); |
| 215 int displayHeight = getResources().getDisplayMetrics().heightPixels; |
| 216 // Clamp within [DEFAULT_HEIGHT_PIXELS, displayHeight] |
| 217 int desiredHeight = |
| 218 Math.min(displayHeight, Math.max(DEFAULT_HEIGHT_PIXELS, requeste
dHeight)); |
| 219 double deviceScaleFactor = ((double)displayHeight) / desiredHeight; |
| 220 Log.d(TAG, "Using scale factor " + deviceScaleFactor + " to set height "
+ desiredHeight); |
| 221 CommandLine.getInstance().appendSwitchWithValue("force-device-scale-fact
or", |
| 222 String.valueOf(deviceScaleFactor)); |
| 223 } |
| 224 |
| 225 private void exitIfUrlMissing() { |
| 226 Intent intent = getIntent(); |
| 227 if (intent != null && intent.getData() != null && !intent.getData().equa
ls(Uri.EMPTY)) { |
| 228 return; |
| 229 } |
| 230 // Log an exception so that the exit cause is obvious when reading the l
ogs. |
| 231 Log.e(TAG, "Activity will not start", |
| 232 new IllegalArgumentException("Intent did not contain a valid url")); |
| 233 System.exit(-1); |
| 234 } |
| 235 |
| 236 /** |
| 237 * @return The currently visible {@link CastWindowAndroid} or null if one is
not showing. |
| 238 */ |
| 239 public CastWindowAndroid getActiveCastWindow() { |
| 240 return mCastWindowManager.getActiveCastWindow(); |
| 241 } |
| 242 |
| 243 /** |
| 244 * @return The {@link ContentViewCore} owned by the currently visible {@link
CastWindowAndroid}, |
| 245 * or null if one is not showing. |
| 246 */ |
| 247 public ContentViewCore getActiveContentViewCore() { |
| 248 CastWindowAndroid shell = getActiveCastWindow(); |
| 249 return shell != null ? shell.getContentViewCore() : null; |
| 250 } |
| 251 |
| 252 @Override |
| 253 public boolean onKeyUp(int keyCode, KeyEvent event) { |
| 254 if (keyCode != KeyEvent.KEYCODE_BACK) { |
| 255 return super.onKeyUp(keyCode, event); |
| 256 } |
| 257 |
| 258 // Just finish this activity to go back to the previous activity or laun
cher. |
| 259 finishGracefully(); |
| 260 return true; |
| 261 } |
| 262 |
| 263 @Override |
| 264 public boolean dispatchKeyEvent(KeyEvent event) { |
| 265 int keyCode = event.getKeyCode(); |
| 266 if (keyCode == KeyEvent.KEYCODE_BACK) { |
| 267 return super.dispatchKeyEvent(event); |
| 268 } |
| 269 return false; |
| 270 } |
| 271 |
| 272 @Override |
| 273 public boolean dispatchGenericMotionEvent(MotionEvent ev) { |
| 274 return false; |
| 275 } |
| 276 |
| 277 @Override |
| 278 public boolean dispatchKeyShortcutEvent(KeyEvent event) { |
| 279 return false; |
| 280 } |
| 281 |
| 282 @Override |
| 283 public boolean dispatchTouchEvent(MotionEvent ev) { |
| 284 return false; |
| 285 } |
| 286 |
| 287 @Override |
| 288 public boolean dispatchTrackballEvent(MotionEvent ev) { |
| 289 return false; |
| 290 } |
| 291 } |
OLD | NEW |