Index: chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java |
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..49a1f14afd152a51e33bb29eafab327e8639a022 |
--- /dev/null |
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastContentWindowAndroid.java |
@@ -0,0 +1,134 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chromecast.shell; |
+ |
+import android.content.BroadcastReceiver; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.content.IntentFilter; |
+import android.net.Uri; |
+import android.os.PatternMatcher; |
+import android.support.v4.content.LocalBroadcastManager; |
+ |
+import org.chromium.base.ContextUtils; |
+import org.chromium.base.Log; |
+import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.base.annotations.JNINamespace; |
+import org.chromium.content_public.browser.WebContents; |
+ |
+/** |
+ * The Java component of CastContentWindowAndroid. This class is responsible for |
+ * starting, stopping and monitoring CastWebContentsActivity. |
+ * |
+ * See chromecast/browser/cast_content_window_android.* for the native half. |
+ */ |
+@JNINamespace("chromecast::shell") |
+public class CastContentWindowAndroid { |
+ private static final String TAG = "cr_CastContentWindowAndroid"; |
+ private static final boolean DEBUG = true; |
+ |
+ // Note: CastContentWindowAndroid may outlive the native object. The native |
+ // ref should be checked that it is not zero before it is used. |
+ private long mNativeCastContentWindowAndroid; |
+ private Context mContext; |
+ private IntentFilter mActivityClosedIntentFilter; |
+ private BroadcastReceiver mActivityClosedBroadcastReceiver; |
+ private String mInstanceId; |
+ |
+ private static int sInstanceId = 1; |
+ |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private static CastContentWindowAndroid create(long nativeCastContentWindowAndroid) { |
+ return new CastContentWindowAndroid( |
+ nativeCastContentWindowAndroid, ContextUtils.getApplicationContext()); |
+ } |
+ |
+ private CastContentWindowAndroid(long nativeCastContentWindowAndroid, Context context) { |
+ mNativeCastContentWindowAndroid = nativeCastContentWindowAndroid; |
+ mContext = context; |
+ mInstanceId = Integer.toString(sInstanceId++); |
+ } |
+ |
+ private Uri getInstanceUri() { |
+ Uri instanceUri = new Uri.Builder() |
+ .scheme(CastWebContentsActivity.ACTION_DATA_SCHEME) |
+ .authority(CastWebContentsActivity.ACTION_DATA_AUTHORITY) |
+ .path(mInstanceId) |
+ .build(); |
+ return instanceUri; |
+ } |
+ |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private void showWebContents(WebContents webContents) { |
+ if (DEBUG) Log.d(TAG, "showWebContents"); |
+ |
+ Intent intent = new Intent( |
+ Intent.ACTION_VIEW, getInstanceUri(), mContext, CastWebContentsActivity.class); |
+ |
+ mActivityClosedBroadcastReceiver = new BroadcastReceiver() { |
+ @Override |
+ public void onReceive(Context context, Intent intent) { |
+ if (intent.getAction() == CastWebContentsActivity.ACTION_ACTIVITY_STOPPED) { |
+ onActivityStopped(); |
+ } else if (intent.getAction() == CastWebContentsActivity.ACTION_KEY_EVENT) { |
+ int keyCode = |
+ intent.getIntExtra(CastWebContentsActivity.ACTION_EXTRA_KEY_CODE, 0); |
+ onKeyDown(keyCode); |
+ } |
+ } |
+ }; |
+ mActivityClosedIntentFilter = new IntentFilter(); |
+ mActivityClosedIntentFilter.addDataScheme(intent.getData().getScheme()); |
+ mActivityClosedIntentFilter.addDataAuthority(intent.getData().getAuthority(), null); |
+ mActivityClosedIntentFilter.addDataPath( |
+ intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL); |
+ mActivityClosedIntentFilter.addAction(CastWebContentsActivity.ACTION_ACTIVITY_STOPPED); |
+ mActivityClosedIntentFilter.addAction(CastWebContentsActivity.ACTION_KEY_EVENT); |
+ LocalBroadcastManager.getInstance(mContext).registerReceiver( |
+ mActivityClosedBroadcastReceiver, mActivityClosedIntentFilter); |
+ |
+ intent.putExtra(CastWebContentsActivity.ACTION_EXTRA_WEB_CONTENTS, webContents); |
+ // FLAG_ACTIVITY_SINGLE_TOP will try to reuse existing activity. |
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK |
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP); |
+ mContext.startActivity(intent); |
+ } |
+ |
+ @SuppressWarnings("unused") |
+ @CalledByNative |
+ private void onNativeDestroyed() { |
+ assert mNativeCastContentWindowAndroid != 0; |
+ mNativeCastContentWindowAndroid = 0; |
+ |
+ // Note: there is a potential race condition when this function is called after |
+ // showWebContents. If the stop intent is received after the start intent but before |
+ // onCreate, the activity won't shutdown. |
+ // TODO(derekjchow): Add a unittest to check this behaviour. Also consider using |
+ // Instrumentation.startActivitySync to guarentee onCreate is run. |
+ |
+ if (DEBUG) Log.d(TAG, "onNativeDestroyed"); |
+ Intent intent = new Intent(CastWebContentsActivity.ACTION_STOP_ACTIVITY, getInstanceUri()); |
+ LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(intent); |
+ } |
+ |
+ private void onActivityStopped() { |
+ if (DEBUG) Log.d(TAG, "onActivityStopped"); |
+ if (mNativeCastContentWindowAndroid != 0) { |
+ nativeOnActivityStopped(mNativeCastContentWindowAndroid); |
+ } |
+ } |
+ |
+ private void onKeyDown(int keyCode) { |
+ if (DEBUG) Log.d(TAG, "onKeyDown"); |
+ if (mNativeCastContentWindowAndroid != 0) { |
+ nativeOnKeyDown(mNativeCastContentWindowAndroid, keyCode); |
+ } |
+ } |
+ |
+ private native void nativeOnActivityStopped(long nativeCastContentWindowAndroid); |
+ private native void nativeOnKeyDown(long nativeCastContentWindowAndroid, int keyCode); |
+} |