Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(343)

Side by Side Diff: chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java

Issue 2570623003: [Chromecast] Turn CastContentWindow into an abstract interface. (Closed)
Patch Set: Set user data outside of contructor Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
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.graphics.Color;
13 import android.media.AudioManager;
14 import android.net.Uri;
15 import android.os.Bundle;
16 import android.os.Handler;
17 import android.os.PatternMatcher;
18 import android.support.v4.content.LocalBroadcastManager;
19 import android.view.KeyEvent;
20 import android.view.MotionEvent;
21 import android.view.WindowManager;
22 import android.widget.FrameLayout;
23 import android.widget.Toast;
24
25 import org.chromium.base.Log;
26 import org.chromium.base.annotations.JNINamespace;
27 import org.chromium.content.browser.ActivityContentVideoViewEmbedder;
28 import org.chromium.content.browser.ContentVideoViewEmbedder;
29 import org.chromium.content.browser.ContentView;
30 import org.chromium.content.browser.ContentViewClient;
31 import org.chromium.content.browser.ContentViewCore;
32 import org.chromium.content.browser.ContentViewRenderView;
33 import org.chromium.content_public.browser.WebContents;
34 import org.chromium.ui.base.ViewAndroidDelegate;
35 import org.chromium.ui.base.WindowAndroid;
36
37 /**
38 * Activity for displaying a WebContents in CastShell.
39 *
40 * Typically, this class is controlled by CastContentWindowAndroid, which will
41 * start a new instance of this activity. If the CastContentWindowAndroid is
42 * destroyed, CastWebContentsActivity should finish(). Similarily, if this
43 * activity is destroyed, CastContentWindowAndroid should be notified by intent.
44 */
45 @JNINamespace("chromecast::shell")
46 public class CastWebContentsActivity extends Activity {
47 private static final String TAG = "cr_CastWebActivity";
48 private static final boolean DEBUG = true;
49
50 private Handler mHandler;
51 private String mInstanceId;
52 private BroadcastReceiver mWindowDestroyedBroadcastReceiver;
53 private IntentFilter mWindowDestroyedIntentFilter;
54 private FrameLayout mCastWebContentsLayout;
55 private AudioManager mAudioManager;
56 private ContentViewClient mContentViewClient;
57 private ContentViewRenderView mContentViewRenderView;
58 private WindowAndroid mWindow;
59 private ContentViewCore mContentViewCore;
60 private ContentView mContentView;
61
62 private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300;
63 public static final String ACTION_DATA_SCHEME = "cast";
64 public static final String ACTION_DATA_AUTHORITY = "webcontents";
65
66 public static final String ACTION_EXTRA_WEB_CONTENTS =
67 "com.google.android.apps.castshell.intent.extra.WEB_CONTENTS";
68 public static final String ACTION_EXTRA_KEY_CODE =
69 "com.google.android.apps.castshell.intent.extra.KEY_CODE";
70 public static final String ACTION_KEY_EVENT =
71 "com.google.android.apps.castshell.intent.action.KEY_EVENT";
72 public static final String ACTION_STOP_ACTIVITY =
73 "com.google.android.apps.castshell.intent.action.STOP_ACTIVITY";
74 public static final String ACTION_ACTIVITY_STOPPED =
75 "com.google.android.apps.castshell.intent.action.ACTIVITY_STOPPED";
76
77 @Override
78 protected void onCreate(final Bundle savedInstanceState) {
79 if (DEBUG) Log.d(TAG, "onCreate");
80 super.onCreate(savedInstanceState);
81
82 mHandler = new Handler();
83
84 // TODO(derekjchow): Remove this call.
85 if (!CastBrowserHelper.initializeBrowser(getApplicationContext())) {
86 Toast.makeText(this, R.string.browser_process_initialization_failed, Toast.LENGTH_SHORT)
87 .show();
88 finish();
89 }
90
91 // Whenever our app is visible, volume controls should modify the music stream.
92 // For more information read:
93 // http://developer.android.com/training/managing-audio/volume-playback. html
94 setVolumeControlStream(AudioManager.STREAM_MUSIC);
95
96 // Set flags to both exit sleep mode when this activity starts and
97 // avoid entering sleep mode while playing media. We cannot distinguish
98 // between video and audio so this applies to both.
99 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
100 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
101
102 mAudioManager = CastAudioManager.getAudioManager(this);
103
104 setContentView(R.layout.cast_web_contents_activity);
105
106 mWindow = new WindowAndroid(this);
107 mContentViewRenderView = new ContentViewRenderView(this) {
108 @Override
109 protected void onReadyToRender() {
110 setOverlayVideoMode(true);
111 }
112 };
113 mContentViewRenderView.onNativeLibraryLoaded(mWindow);
114 // Setting the background color to black avoids rendering a white splash screen
115 // before the players are loaded. See crbug/307113 for details.
116 mContentViewRenderView.setSurfaceViewBackgroundColor(Color.BLACK);
117
118 mCastWebContentsLayout = (FrameLayout) findViewById(R.id.web_contents_co ntainer);
119 mCastWebContentsLayout.addView(mContentViewRenderView,
120 new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARE NT,
121 FrameLayout.LayoutParams.MATCH_PARENT));
122
123 Intent intent = getIntent();
124 handleIntent(intent);
125 }
126
127 protected void handleIntent(Intent intent) {
128 intent.setExtrasClassLoader(WebContents.class.getClassLoader());
129 mInstanceId = intent.getData().getPath();
130
131 final String instanceId = mInstanceId;
132 if (mWindowDestroyedBroadcastReceiver != null) {
133 LocalBroadcastManager.getInstance(this).unregisterReceiver(
134 mWindowDestroyedBroadcastReceiver);
135 }
136 mWindowDestroyedBroadcastReceiver = new BroadcastReceiver() {
137 @Override
138 public void onReceive(Context context, Intent intent) {
139 detachWebContentsIfAny();
140 maybeFinishLater();
141 }
142 };
143 mWindowDestroyedIntentFilter = new IntentFilter();
144 mWindowDestroyedIntentFilter.addDataScheme(intent.getData().getScheme()) ;
145 mWindowDestroyedIntentFilter.addDataAuthority(intent.getData().getAuthor ity(), null);
146 mWindowDestroyedIntentFilter.addDataPath(mInstanceId, PatternMatcher.PAT TERN_LITERAL);
147 mWindowDestroyedIntentFilter.addAction(ACTION_STOP_ACTIVITY);
148 LocalBroadcastManager.getInstance(this).registerReceiver(
149 mWindowDestroyedBroadcastReceiver, mWindowDestroyedIntentFilter) ;
150
151 WebContents webContents =
152 (WebContents) intent.getParcelableExtra(ACTION_EXTRA_WEB_CONTENT S);
153 if (webContents == null) {
154 Log.e(TAG, "Received null WebContents in intent.");
155 maybeFinishLater();
156 return;
157 }
158
159 detachWebContentsIfAny();
160 showWebContents(webContents);
161 }
162
163 @Override
164 protected void onNewIntent(Intent intent) {
165 if (DEBUG) Log.d(TAG, "onNewIntent");
166
167 // If we're currently finishing this activity, we should start a new act ivity to
168 // display the new app.
169 if (isFinishing()) {
170 Log.d(TAG, "Activity is finishing, starting new activity.");
171 int flags = intent.getFlags();
172 flags = flags & ~Intent.FLAG_ACTIVITY_SINGLE_TOP;
173 intent.setFlags(flags);
174 startActivity(intent);
175 return;
176 }
177
178 handleIntent(intent);
179 }
180
181 @Override
182 protected void onDestroy() {
183 if (DEBUG) Log.d(TAG, "onDestroy");
184 super.onDestroy();
185 }
186
187 @Override
188 protected void onStart() {
189 if (DEBUG) Log.d(TAG, "onStart");
190 super.onStart();
191 }
192
193 @Override
194 protected void onStop() {
195 if (DEBUG) Log.d(TAG, "onStop");
196
197 detachWebContentsIfAny();
198 releaseStreamMuteIfNecessary();
199 super.onStop();
200 }
201
202 @Override
203 protected void onResume() {
204 if (DEBUG) Log.d(TAG, "onResume");
205 super.onResume();
206
207 if (mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC,
208 AudioManager.AUDIOFOCUS_GAIN) != AudioManager.AUDIOFOCUS_REQ UEST_GRANTED) {
209 Log.e(TAG, "Failed to obtain audio focus");
210 }
211 }
212
213 @Override
214 protected void onPause() {
215 if (DEBUG) Log.d(TAG, "onPause");
216 super.onPause();
217
218 // Release the audio focus. Note that releasing audio focus does not sto p audio playback,
219 // it just notifies the framework that this activity has stopped playing audio.
220 if (mAudioManager.abandonAudioFocus(null) != AudioManager.AUDIOFOCUS_REQ UEST_GRANTED) {
221 Log.e(TAG, "Failed to abandon audio focus");
222 }
223 }
224
225 @Override
226 public boolean dispatchKeyEvent(KeyEvent event) {
227 if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
228 int keyCode = event.getKeyCode();
229 int action = event.getAction();
230
231 // Similar condition for all single-click events.
232 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
233 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
234 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
235 || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY
236 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE
237 || keyCode == KeyEvent.KEYCODE_MEDIA_STOP
238 || keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
239 || keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
240 Intent intent = new Intent(ACTION_KEY_EVENT, getInstanceUri());
241 intent.putExtra(ACTION_EXTRA_KEY_CODE, keyCode);
242 LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent );
243 return true;
244 }
245 }
246
247 if (keyCode == KeyEvent.KEYCODE_BACK) {
248 return super.dispatchKeyEvent(event);
249 }
250 return false;
251 }
252
253 @Override
254 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
255 return false;
256 }
257
258 @Override
259 public boolean dispatchKeyShortcutEvent(KeyEvent event) {
260 return false;
261 }
262
263 @Override
264 public boolean dispatchTouchEvent(MotionEvent ev) {
265 return false;
266 }
267
268 @Override
269 public boolean dispatchTrackballEvent(MotionEvent ev) {
270 return false;
271 }
272
273 @SuppressWarnings("deprecation")
274 private void releaseStreamMuteIfNecessary() {
275 AudioManager audioManager = CastAudioManager.getAudioManager(this);
276 boolean isMuted = false;
277 try {
278 isMuted = (Boolean) audioManager.getClass()
279 .getMethod("isStreamMute", int.class)
byungchul 2016/12/16 22:52:22 indentation?
derekjchow1 2016/12/17 00:13:07 See other comment (clang-format-diff did it)!
280 .invoke(audioManager, AudioManager.STREAM_MUSIC);
281 } catch (Exception e) {
282 Log.e(TAG, "Cannot call AudioManager.isStreamMute().", e);
283 }
284
285 if (isMuted) {
286 // Note: this is a no-op on fixed-volume devices.
287 audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
288 }
289 }
290
291 // Closes this activity if a new WebContents is not being displayed.
292 private void maybeFinishLater() {
293 Log.d(TAG, "maybeFinishLater");
294 final String currentInstanceId = mInstanceId;
295 mHandler.postDelayed(new Runnable() {
296 @Override
297 public void run() {
298 if (currentInstanceId == mInstanceId) {
299 Log.d(TAG, "Finishing.");
300 finish();
301 }
302 }
303 }, TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS);
304 }
305
306 // Sets webContents to be the currently displayed webContents.
307 private void showWebContents(WebContents webContents) {
308 if (DEBUG) Log.d(TAG, "showWebContents");
309
310 // Set ContentVideoViewEmbedder to allow video playback.
311 nativeSetContentVideoViewEmbedder(webContents, new ActivityContentVideoV iewEmbedder(this));
312
313 // TODO(derekjchow): productVersion
314 mContentViewCore = new ContentViewCore(this, "");
315 mContentView = ContentView.createContentView(this, mContentViewCore);
316 mContentViewCore.initialize(ViewAndroidDelegate.createBasicDelegate(mCon tentView),
317 mContentView, webContents, mWindow);
318 mContentViewClient = new ContentViewClient();
319 mContentViewCore.setContentViewClient(mContentViewClient);
320 // Enable display of current webContents.
321 if (getParent() != null) mContentViewCore.onShow();
322 mCastWebContentsLayout.addView(
323 mContentView, new FrameLayout.LayoutParams(FrameLayout.LayoutPar ams.MATCH_PARENT,
324 FrameLayout.LayoutParams.MATCH_PARENT));
byungchul 2016/12/16 22:52:22 This style doesn't look readable
derekjchow1 2016/12/17 00:13:07 ditto.
325 mContentView.requestFocus();
326 mContentViewRenderView.setCurrentContentViewCore(mContentViewCore);
327 }
328
329 // Remove the currently displayed webContents. no-op if nothing is being dis played.
330 private void detachWebContentsIfAny() {
331 if (DEBUG) Log.d(TAG, "detachWebContentsIfAny");
332 if (mContentView != null) {
333 mCastWebContentsLayout.removeView(mContentView);
334 mContentView = null;
335 mContentViewCore = null;
336
337 // Inform CastContentWindowAndroid we're detaching.
338 Intent intent = new Intent(ACTION_ACTIVITY_STOPPED, getInstanceUri() );
339 LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent);
340 }
341 }
342
343 private Uri getInstanceUri() {
344 Uri instanceUri = new Uri.Builder()
345 .scheme(CastWebContentsActivity.ACTION_DATA_SC HEME)
byungchul 2016/12/16 22:52:22 ditto
derekjchow1 2016/12/17 00:13:07 ditto
346 .authority(CastWebContentsActivity.ACTION_DATA _AUTHORITY)
347 .path(mInstanceId)
348 .build();
349 return instanceUri;
350 }
351
352 private native void nativeSetContentVideoViewEmbedder(
353 WebContents webContents, ContentVideoViewEmbedder embedder);
354 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698