Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / chromecast / shell / android / apk / src / org / chromium / chromecast / shell / CastShellActivity.java
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 and CastWindow having
47     // this native shell instance.
48     private long mNativeCastWindow;
49
50     /**
51      * Returns whether or not CastShellActivity should launch the browser startup 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_container);
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, false /* gracefully */);
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 managing
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 the 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 immediately tear
162         // everything down. Apps should not continue running and playing sound in the background.
163         super.onPause();
164
165         // Release the audio focus. Note that releasing audio focus does not stop audio playback,
166         // it just notifies the framework that this activity has stopped playing audio.
167         if (mAudioManager.abandonAudioFocus(null) != AudioManager.AUDIOFOCUS_REQUEST_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, true /* gracefully */);
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, devtoolsBroadcastIntentFilter);
205     }
206
207     private void unregisterBroadcastReceiver() {
208         LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(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, requestedHeight));
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-factor",
222                 String.valueOf(deviceScaleFactor));
223     }
224
225     private void exitIfUrlMissing() {
226         Intent intent = getIntent();
227         if (intent != null && intent.getData() != null && !intent.getData().equals(Uri.EMPTY)) {
228             return;
229         }
230         // Log an exception so that the exit cause is obvious when reading the logs.
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 launcher.
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 }