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