Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chromecast.shell; | 5 package org.chromium.chromecast.shell; |
| 6 | 6 |
| 7 import android.app.Activity; | 7 import android.app.Activity; |
| 8 import android.content.BroadcastReceiver; | 8 import android.content.BroadcastReceiver; |
| 9 import android.content.Context; | 9 import android.content.Context; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.IntentFilter; | 11 import android.content.IntentFilter; |
| 12 import android.graphics.Color; | 12 import android.graphics.Color; |
| 13 import android.media.AudioManager; | 13 import android.media.AudioManager; |
| 14 import android.net.Uri; | |
| 15 import android.os.Bundle; | 14 import android.os.Bundle; |
| 16 import android.os.Handler; | 15 import android.os.Handler; |
| 17 import android.os.PatternMatcher; | 16 import android.os.PatternMatcher; |
| 18 import android.support.v4.content.LocalBroadcastManager; | 17 import android.support.v4.content.LocalBroadcastManager; |
| 19 import android.view.KeyEvent; | 18 import android.view.KeyEvent; |
| 20 import android.view.MotionEvent; | 19 import android.view.MotionEvent; |
| 21 import android.view.WindowManager; | 20 import android.view.WindowManager; |
| 22 import android.widget.FrameLayout; | 21 import android.widget.FrameLayout; |
| 23 import android.widget.Toast; | |
| 24 | 22 |
| 25 import org.chromium.base.Log; | 23 import org.chromium.base.Log; |
| 26 import org.chromium.base.annotations.JNINamespace; | 24 import org.chromium.base.annotations.JNINamespace; |
| 27 import org.chromium.content.browser.ActivityContentVideoViewEmbedder; | 25 import org.chromium.content.browser.ActivityContentVideoViewEmbedder; |
| 28 import org.chromium.content.browser.ContentVideoViewEmbedder; | 26 import org.chromium.content.browser.ContentVideoViewEmbedder; |
| 29 import org.chromium.content.browser.ContentView; | 27 import org.chromium.content.browser.ContentView; |
| 30 import org.chromium.content.browser.ContentViewCore; | 28 import org.chromium.content.browser.ContentViewCore; |
| 31 import org.chromium.content.browser.ContentViewRenderView; | 29 import org.chromium.content.browser.ContentViewRenderView; |
| 32 import org.chromium.content_public.browser.WebContents; | 30 import org.chromium.content_public.browser.WebContents; |
| 33 import org.chromium.ui.base.ViewAndroidDelegate; | 31 import org.chromium.ui.base.ViewAndroidDelegate; |
| 34 import org.chromium.ui.base.WindowAndroid; | 32 import org.chromium.ui.base.WindowAndroid; |
| 35 | 33 |
| 36 /** | 34 /** |
| 37 * Activity for displaying a WebContents in CastShell. | 35 * Activity for displaying a WebContents in CastShell. |
| 38 * | 36 * <p> |
| 39 * Typically, this class is controlled by CastContentWindowAndroid, which will | 37 * Typically, this class is controlled by CastContentWindowAndroid, which will |
| 40 * start a new instance of this activity. If the CastContentWindowAndroid is | 38 * start a new instance of this activity. If the CastContentWindowAndroid is |
| 41 * destroyed, CastWebContentsActivity should finish(). Similarily, if this | 39 * destroyed, CastWebContentsActivity should finish(). Similarily, if this |
| 42 * activity is destroyed, CastContentWindowAndroid should be notified by intent. | 40 * activity is destroyed, CastContentWindowAndroid should be notified by intent. |
| 43 */ | 41 */ |
| 44 @JNINamespace("chromecast::shell") | 42 @JNINamespace("chromecast::shell") |
| 45 public class CastWebContentsActivity extends Activity { | 43 public class CastWebContentsActivity extends Activity { |
| 46 private static final String TAG = "cr_CastWebActivity"; | 44 private static final String TAG = "cr_CastWebActivity"; |
| 47 private static final boolean DEBUG = true; | 45 private static final boolean DEBUG = true; |
| 48 | 46 |
| 49 private Handler mHandler; | 47 private Handler mHandler; |
| 50 private String mInstanceId; | 48 private String mInstanceId; |
| 51 private BroadcastReceiver mWindowDestroyedBroadcastReceiver; | 49 private BroadcastReceiver mWindowDestroyedBroadcastReceiver; |
| 52 private IntentFilter mWindowDestroyedIntentFilter; | 50 private IntentFilter mWindowDestroyedIntentFilter; |
| 53 private FrameLayout mCastWebContentsLayout; | 51 private FrameLayout mCastWebContentsLayout; |
| 54 private AudioManager mAudioManager; | 52 private AudioManager mAudioManager; |
| 55 private ContentViewRenderView mContentViewRenderView; | 53 private ContentViewRenderView mContentViewRenderView; |
| 56 private WindowAndroid mWindow; | 54 private WindowAndroid mWindow; |
| 57 private ContentViewCore mContentViewCore; | 55 private ContentViewCore mContentViewCore; |
| 58 private ContentView mContentView; | 56 private ContentView mContentView; |
| 59 private boolean mReceivedUserLeave = false; | 57 private boolean mReceivedUserLeave = false; |
| 60 | 58 |
| 61 private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300; | 59 private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300; |
| 62 public static final String ACTION_DATA_SCHEME = "cast"; | |
| 63 public static final String ACTION_DATA_AUTHORITY = "webcontents"; | |
| 64 | 60 |
| 65 public static final String ACTION_EXTRA_WEB_CONTENTS = | |
| 66 "com.google.android.apps.castshell.intent.extra.WEB_CONTENTS"; | |
| 67 public static final String ACTION_EXTRA_KEY_CODE = | |
| 68 "com.google.android.apps.castshell.intent.extra.KEY_CODE"; | |
| 69 public static final String ACTION_KEY_EVENT = | |
| 70 "com.google.android.apps.castshell.intent.action.KEY_EVENT"; | |
| 71 public static final String ACTION_STOP_ACTIVITY = | 61 public static final String ACTION_STOP_ACTIVITY = |
| 72 "com.google.android.apps.castshell.intent.action.STOP_ACTIVITY"; | 62 "com.google.android.apps.castshell.intent.action.STOP_ACTIVITY"; |
| 73 public static final String ACTION_ACTIVITY_STOPPED = | |
| 74 "com.google.android.apps.castshell.intent.action.ACTIVITY_STOPPED"; | |
| 75 | 63 |
| 76 /* | 64 /* |
| 77 * Intended to be called from "onStop" to determine if this is a "legitimate " stop or not. | 65 * Intended to be called from "onStop" to determine if this is a "legitimate " stop or not. |
| 78 * When starting CastShellActivity from the TV in sleep mode, an extra onPau se/onStop will be | 66 * When starting CastShellActivity from the TV in sleep mode, an extra onPau se/onStop will be |
| 79 * fired. | 67 * fired. |
| 80 * Details: http://stackoverflow.com/questions/25369909/ | 68 * Details: http://stackoverflow.com/questions/25369909/ |
| 81 * We use onUserLeaveHint to determine if the onPause/onStop called because of user intent. | 69 * We use onUserLeaveHint to determine if the onPause/onStop called because of user intent. |
| 82 */ | 70 */ |
| 83 private boolean isStopping() { | 71 private boolean isStopping() { |
| 84 return mReceivedUserLeave; | 72 return mReceivedUserLeave; |
| 85 } | 73 } |
| 86 | 74 |
| 87 @Override | 75 @Override |
| 88 protected void onCreate(final Bundle savedInstanceState) { | 76 protected void onCreate(final Bundle savedInstanceState) { |
| 89 if (DEBUG) Log.d(TAG, "onCreate"); | 77 if (DEBUG) Log.d(TAG, "onCreate"); |
| 90 super.onCreate(savedInstanceState); | 78 super.onCreate(savedInstanceState); |
| 91 | 79 |
| 92 mHandler = new Handler(); | 80 mHandler = new Handler(); |
| 93 | 81 |
| 94 // TODO(derekjchow): Remove this call. | |
| 95 if (!CastBrowserHelper.initializeBrowser(getApplicationContext())) { | |
|
halliwell
2017/05/19 17:04:08
What's the story behind removing this?
thoren
2017/05/19 17:19:49
Mostly because the TODO said to and it didn't seem
halliwell
2017/05/19 18:14:06
I see. I think we still want upstream CastShell t
| |
| 96 Toast.makeText(this, R.string.browser_process_initialization_failed, Toast.LENGTH_SHORT) | |
| 97 .show(); | |
| 98 finish(); | |
| 99 } | |
| 100 | |
| 101 // Whenever our app is visible, volume controls should modify the music stream. | 82 // Whenever our app is visible, volume controls should modify the music stream. |
| 102 // For more information read: | 83 // For more information read: |
| 103 // http://developer.android.com/training/managing-audio/volume-playback. html | 84 // http://developer.android.com/training/managing-audio/volume-playback. html |
| 104 setVolumeControlStream(AudioManager.STREAM_MUSIC); | 85 setVolumeControlStream(AudioManager.STREAM_MUSIC); |
| 105 | 86 |
| 106 // Set flags to both exit sleep mode when this activity starts and | 87 // Set flags to both exit sleep mode when this activity starts and |
| 107 // avoid entering sleep mode while playing media. We cannot distinguish | 88 // avoid entering sleep mode while playing media. We cannot distinguish |
| 108 // between video and audio so this applies to both. | 89 // between video and audio so this applies to both. |
| 109 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); | 90 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); |
| 110 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); | 91 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 131 FrameLayout.LayoutParams.MATCH_PARENT)); | 112 FrameLayout.LayoutParams.MATCH_PARENT)); |
| 132 | 113 |
| 133 Intent intent = getIntent(); | 114 Intent intent = getIntent(); |
| 134 handleIntent(intent); | 115 handleIntent(intent); |
| 135 } | 116 } |
| 136 | 117 |
| 137 protected void handleIntent(Intent intent) { | 118 protected void handleIntent(Intent intent) { |
| 138 intent.setExtrasClassLoader(WebContents.class.getClassLoader()); | 119 intent.setExtrasClassLoader(WebContents.class.getClassLoader()); |
| 139 mInstanceId = intent.getData().getPath(); | 120 mInstanceId = intent.getData().getPath(); |
| 140 | 121 |
| 141 final String instanceId = mInstanceId; | |
| 142 if (mWindowDestroyedBroadcastReceiver != null) { | 122 if (mWindowDestroyedBroadcastReceiver != null) { |
| 143 LocalBroadcastManager.getInstance(this).unregisterReceiver( | 123 LocalBroadcastManager.getInstance(this).unregisterReceiver( |
| 144 mWindowDestroyedBroadcastReceiver); | 124 mWindowDestroyedBroadcastReceiver); |
| 145 } | 125 } |
| 146 mWindowDestroyedBroadcastReceiver = new BroadcastReceiver() { | 126 mWindowDestroyedBroadcastReceiver = new BroadcastReceiver() { |
| 147 @Override | 127 @Override |
| 148 public void onReceive(Context context, Intent intent) { | 128 public void onReceive(Context context, Intent intent) { |
| 149 detachWebContentsIfAny(); | 129 detachWebContentsIfAny(); |
| 150 maybeFinishLater(); | 130 maybeFinishLater(); |
| 151 } | 131 } |
| 152 }; | 132 }; |
| 153 mWindowDestroyedIntentFilter = new IntentFilter(); | 133 mWindowDestroyedIntentFilter = new IntentFilter(); |
| 154 mWindowDestroyedIntentFilter.addDataScheme(intent.getData().getScheme()) ; | 134 mWindowDestroyedIntentFilter.addDataScheme(intent.getData().getScheme()) ; |
| 155 mWindowDestroyedIntentFilter.addDataAuthority(intent.getData().getAuthor ity(), null); | 135 mWindowDestroyedIntentFilter.addDataAuthority(intent.getData().getAuthor ity(), null); |
| 156 mWindowDestroyedIntentFilter.addDataPath(mInstanceId, PatternMatcher.PAT TERN_LITERAL); | 136 mWindowDestroyedIntentFilter.addDataPath(mInstanceId, PatternMatcher.PAT TERN_LITERAL); |
| 157 mWindowDestroyedIntentFilter.addAction(ACTION_STOP_ACTIVITY); | 137 mWindowDestroyedIntentFilter.addAction(ACTION_STOP_ACTIVITY); |
| 158 LocalBroadcastManager.getInstance(this).registerReceiver( | 138 LocalBroadcastManager.getInstance(this).registerReceiver( |
| 159 mWindowDestroyedBroadcastReceiver, mWindowDestroyedIntentFilter) ; | 139 mWindowDestroyedBroadcastReceiver, mWindowDestroyedIntentFilter) ; |
| 160 | 140 |
| 161 WebContents webContents = | 141 WebContents webContents = (WebContents) intent.getParcelableExtra( |
| 162 (WebContents) intent.getParcelableExtra(ACTION_EXTRA_WEB_CONTENT S); | 142 CastWebContentsComponent.ACTION_EXTRA_WEB_CONTENTS); |
| 163 if (webContents == null) { | 143 if (webContents == null) { |
| 164 Log.e(TAG, "Received null WebContents in intent."); | 144 Log.e(TAG, "Received null WebContents in intent."); |
| 165 maybeFinishLater(); | 145 maybeFinishLater(); |
| 166 return; | 146 return; |
| 167 } | 147 } |
| 168 | 148 |
| 169 detachWebContentsIfAny(); | 149 detachWebContentsIfAny(); |
| 170 showWebContents(webContents); | 150 showWebContents(webContents); |
| 171 } | 151 } |
| 172 | 152 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 releaseStreamMuteIfNecessary(); | 188 releaseStreamMuteIfNecessary(); |
| 209 } | 189 } |
| 210 super.onStop(); | 190 super.onStop(); |
| 211 } | 191 } |
| 212 | 192 |
| 213 @Override | 193 @Override |
| 214 protected void onResume() { | 194 protected void onResume() { |
| 215 if (DEBUG) Log.d(TAG, "onResume"); | 195 if (DEBUG) Log.d(TAG, "onResume"); |
| 216 super.onResume(); | 196 super.onResume(); |
| 217 | 197 |
| 218 if (mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, | 198 if (mAudioManager.requestAudioFocus( |
| 219 AudioManager.AUDIOFOCUS_GAIN) != AudioManager.AUDIOFOCUS_REQ UEST_GRANTED) { | 199 null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAI N) |
| 200 != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { | |
| 220 Log.e(TAG, "Failed to obtain audio focus"); | 201 Log.e(TAG, "Failed to obtain audio focus"); |
| 221 } | 202 } |
| 222 } | 203 } |
| 223 | 204 |
| 224 @Override | 205 @Override |
| 225 protected void onPause() { | 206 protected void onPause() { |
| 226 if (DEBUG) Log.d(TAG, "onPause"); | 207 if (DEBUG) Log.d(TAG, "onPause"); |
| 227 super.onPause(); | 208 super.onPause(); |
| 228 | 209 |
| 229 // Release the audio focus. Note that releasing audio focus does not sto p audio playback, | 210 // Release the audio focus. Note that releasing audio focus does not sto p audio playback, |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 247 | 228 |
| 248 // Similar condition for all single-click events. | 229 // Similar condition for all single-click events. |
| 249 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { | 230 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { |
| 250 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER | 231 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER |
| 251 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE | 232 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE |
| 252 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY | 233 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY |
| 253 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE | 234 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE |
| 254 || keyCode == KeyEvent.KEYCODE_MEDIA_STOP | 235 || keyCode == KeyEvent.KEYCODE_MEDIA_STOP |
| 255 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT | 236 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT |
| 256 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) { | 237 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) { |
| 257 Intent intent = new Intent(ACTION_KEY_EVENT, getInstanceUri()); | 238 CastWebContentsComponent.onKeyDown(this, mInstanceId, keyCode); |
| 258 intent.putExtra(ACTION_EXTRA_KEY_CODE, keyCode); | |
| 259 LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent ); | |
| 260 | 239 |
| 261 // Stop key should end the entire session. | 240 // Stop key should end the entire session. |
| 262 if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { | 241 if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { |
| 263 mReceivedUserLeave = true; | 242 mReceivedUserLeave = true; |
| 264 finish(); | 243 finish(); |
| 265 } | 244 } |
| 266 | 245 |
| 267 return true; | 246 return true; |
| 268 } | 247 } |
| 269 } | 248 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 // Set ContentVideoViewEmbedder to allow video playback. | 314 // Set ContentVideoViewEmbedder to allow video playback. |
| 336 nativeSetContentVideoViewEmbedder(webContents, new ActivityContentVideoV iewEmbedder(this)); | 315 nativeSetContentVideoViewEmbedder(webContents, new ActivityContentVideoV iewEmbedder(this)); |
| 337 | 316 |
| 338 // TODO(derekjchow): productVersion | 317 // TODO(derekjchow): productVersion |
| 339 mContentViewCore = new ContentViewCore(this, ""); | 318 mContentViewCore = new ContentViewCore(this, ""); |
| 340 mContentView = ContentView.createContentView(this, mContentViewCore); | 319 mContentView = ContentView.createContentView(this, mContentViewCore); |
| 341 mContentViewCore.initialize(ViewAndroidDelegate.createBasicDelegate(mCon tentView), | 320 mContentViewCore.initialize(ViewAndroidDelegate.createBasicDelegate(mCon tentView), |
| 342 mContentView, webContents, mWindow); | 321 mContentView, webContents, mWindow); |
| 343 // Enable display of current webContents. | 322 // Enable display of current webContents. |
| 344 if (getParent() != null) mContentViewCore.onShow(); | 323 if (getParent() != null) mContentViewCore.onShow(); |
| 345 mCastWebContentsLayout.addView( | 324 mCastWebContentsLayout.addView(mContentView, |
| 346 mContentView, new FrameLayout.LayoutParams(FrameLayout.LayoutPar ams.MATCH_PARENT, | 325 new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARE NT, |
| 347 FrameLayout.LayoutPar ams.MATCH_PARENT)); | 326 FrameLayout.LayoutParams.MATCH_PARENT)); |
| 348 mContentView.requestFocus(); | 327 mContentView.requestFocus(); |
| 349 mContentViewRenderView.setCurrentContentViewCore(mContentViewCore); | 328 mContentViewRenderView.setCurrentContentViewCore(mContentViewCore); |
| 350 } | 329 } |
| 351 | 330 |
| 352 // Remove the currently displayed webContents. no-op if nothing is being dis played. | 331 // Remove the currently displayed webContents. no-op if nothing is being dis played. |
| 353 private void detachWebContentsIfAny() { | 332 private void detachWebContentsIfAny() { |
| 354 if (DEBUG) Log.d(TAG, "detachWebContentsIfAny"); | 333 if (DEBUG) Log.d(TAG, "detachWebContentsIfAny"); |
| 355 if (mContentView != null) { | 334 if (mContentView != null) { |
| 356 mCastWebContentsLayout.removeView(mContentView); | 335 mCastWebContentsLayout.removeView(mContentView); |
| 357 mContentView = null; | 336 mContentView = null; |
| 358 mContentViewCore = null; | 337 mContentViewCore = null; |
| 359 | 338 |
| 360 // Inform CastContentWindowAndroid we're detaching. | 339 CastWebContentsComponent.onComponentClosed(this, mInstanceId); |
| 361 Intent intent = new Intent(ACTION_ACTIVITY_STOPPED, getInstanceUri() ); | |
| 362 LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent); | |
| 363 } | 340 } |
| 364 } | 341 } |
| 365 | 342 |
| 366 private Uri getInstanceUri() { | |
| 367 Uri instanceUri = new Uri.Builder() | |
| 368 .scheme(CastWebContentsActivity.ACTION_DATA_SCHEME) | |
| 369 .authority(CastWebContentsActivity.ACTION_DATA_AUTHORITY) | |
| 370 .path(mInstanceId) | |
| 371 .build(); | |
| 372 return instanceUri; | |
| 373 } | |
| 374 | |
| 375 private native void nativeSetContentVideoViewEmbedder( | 343 private native void nativeSetContentVideoViewEmbedder( |
| 376 WebContents webContents, ContentVideoViewEmbedder embedder); | 344 WebContents webContents, ContentVideoViewEmbedder embedder); |
| 377 } | 345 } |
| OLD | NEW |