Commit 180905f5 authored by Zhiheng Vincent Li's avatar Zhiheng Vincent Li Committed by Commit Bot

[Chromecast] Add interfaces to allow cast apps communicate with Android codes

Main interfaces added:

1. Add methods in CastContentWindow and CastContentWindow.Delegate for Cast Activity and WebApplication

2. Add methods in CastContentWindowAndroid for JNI calls between Java and C++

3. Add some methods and intent types in CastWebContentComponent for calling between CastContentWindowAndroid and CastWebContentComponent, and between CastWebContentComponent and CastWebContentsFragment

4. Add some intent types for calling between CastWebContentComponent/CastWebContentsFragment and external activity.

Design doc: go/cast-to-android

Test: cast_shell_junit_tests browsertests

Bug: b/65100261 b/72227153 b/72436346 b/72237087 b/72912957 b/72436104 b/72712171

Change-Id: I09056f6fe78341e545c5e1342640a4dfddff28f4
Reviewed-on: https://chromium-review.googlesource.com/940809
Commit-Queue: Zhiheng(Vincent) Li <vincentli@google.com>
Reviewed-by: default avatarLuke Halliwell <halliwell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543204}
parent 3e2452d4
......@@ -103,10 +103,11 @@ android_library("cast_shell_java") {
"$java_src_dir/org/chromium/chromecast/shell/CastMetricsHelper.java",
"$java_src_dir/org/chromium/chromecast/shell/CastSysInfoAndroid.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsActivity.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsComponent.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsFragment.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsIntentUtils.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsService.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsSurfaceHelper.java",
"$java_src_dir/org/chromium/chromecast/shell/CastWebContentsComponent.java",
"$java_src_dir/org/chromium/chromecast/shell/LogcatElision.java",
"$java_src_dir/org/chromium/chromecast/shell/ElidedLogcatProvider.java",
]
......@@ -148,6 +149,7 @@ junit_binary("cast_shell_junit_tests") {
"junit/src/org/chromium/chromecast/shell/CastAudioManagerTest.java",
"junit/src/org/chromium/chromecast/shell/CastWebContentsActivityTest.java",
"junit/src/org/chromium/chromecast/shell/CastWebContentsComponentTest.java",
"junit/src/org/chromium/chromecast/shell/CastWebContentsIntentUtilsTest.java",
"junit/src/org/chromium/chromecast/shell/LocalBroadcastReceiverScopeTest.java",
"junit/src/org/chromium/chromecast/shell/LogcatElisionUnitTest.java",
"junit/src/org/chromium/chromecast/shell/ElidedLogcatProviderUnitTest.java",
......
......@@ -20,7 +20,8 @@ import org.chromium.content_public.browser.WebContents;
*/
@JNINamespace("chromecast::shell")
public class CastContentWindowAndroid implements CastWebContentsComponent.OnComponentClosedHandler,
CastWebContentsComponent.OnKeyDownHandler {
CastWebContentsComponent.OnKeyDownHandler,
CastWebContentsComponent.SurfaceEventHandler {
private static final String TAG = "cr_CastContentWindowAndroid";
private static final boolean DEBUG = true;
......@@ -46,17 +47,18 @@ public class CastContentWindowAndroid implements CastWebContentsComponent.OnComp
mNativeCastContentWindowAndroid = nativeCastContentWindowAndroid;
mContext = context;
mInstanceId = Integer.toString(sInstanceId++);
mComponent =
new CastWebContentsComponent(mInstanceId, this, this, isHeadless, enableTouchInput);
// TODO call nativeGetId() to set ID to CastWebContentsComponent.
mComponent = new CastWebContentsComponent(
mInstanceId, this, this, this, isHeadless, enableTouchInput);
}
@SuppressWarnings("unused")
@CalledByNative
private void createWindowForWebContents(WebContents webContents) {
private void createWindowForWebContents(WebContents webContents, int visisbilityPriority) {
if (DEBUG) Log.d(TAG, "createWindowForWebContents");
mComponent.start(mContext, webContents);
String appId = nativeGetId(mNativeCastContentWindowAndroid);
mComponent.start(new CastWebContentsComponent.StartParams(
mContext, webContents, appId, visisbilityPriority));
}
@SuppressWarnings("unused")
......@@ -75,6 +77,18 @@ public class CastContentWindowAndroid implements CastWebContentsComponent.OnComp
mComponent.stop(mContext);
}
@SuppressWarnings("unused")
@CalledByNative
private void requestVisibilityPriority(int visisbilityPriority) {
mComponent.requestVisibilityPriority(visisbilityPriority);
}
@SuppressWarnings("unused")
@CalledByNative
private void requestMoveOut() {
mComponent.requestMoveOut();
}
@Override
public void onKeyDown(int keyCode) {
if (DEBUG) Log.d(TAG, "onKeyDown");
......@@ -91,7 +105,32 @@ public class CastContentWindowAndroid implements CastWebContentsComponent.OnComp
}
}
@Override
public void onVisibilityChange(int visibilityType) {
if (DEBUG) Log.d(TAG, "onVisibilityChange");
if (mNativeCastContentWindowAndroid != 0) {
nativeOnVisibilityChange(mNativeCastContentWindowAndroid, visibilityType);
}
}
@Override
public boolean consumeGesture(int gestureType) {
if (DEBUG) Log.d(TAG, "onVisibilityChange");
if (mNativeCastContentWindowAndroid != 0) {
return nativeConsumeGesture(mNativeCastContentWindowAndroid, gestureType);
}
return false;
}
private native void nativeOnActivityStopped(long nativeCastContentWindowAndroid);
private native void nativeOnKeyDown(long nativeCastContentWindowAndroid, int keyCode);
private native boolean nativeConsumeGesture(
long nativeCastContentWindowAndroid, int gestureType);
private native void nativeOnVisibilityChange(
long nativeCastContentWindowAndroid, int visibilityType);
private native String nativeGetId(long nativeCastContentWindowAndroid);
}
......@@ -124,7 +124,7 @@ public class CastWebContentsActivity extends Activity {
Log.i(TAG, "Intent without bundle received!");
return;
}
final String uriString = bundle.getString(CastWebContentsComponent.INTENT_EXTRA_URI);
final String uriString = CastWebContentsIntentUtils.getUriString(intent);
if (uriString == null) {
Log.i(TAG, "Intent without uri received!");
return;
......@@ -140,11 +140,9 @@ public class CastWebContentsActivity extends Activity {
}
bundle.setClassLoader(WebContents.class.getClassLoader());
final WebContents webContents = (WebContents) bundle.getParcelable(
CastWebContentsComponent.ACTION_EXTRA_WEB_CONTENTS);
final WebContents webContents = CastWebContentsIntentUtils.getWebContents(intent);
boolean touchInputEnabled =
bundle.getBoolean(CastWebContentsComponent.ACTION_EXTRA_TOUCH_INPUT_ENABLED, false);
final boolean touchInputEnabled = CastWebContentsIntentUtils.isTouchable(intent);
mSurfaceHelper.onNewWebContents(uri, webContents, touchInputEnabled);
}
......@@ -224,7 +222,7 @@ public class CastWebContentsActivity extends Activity {
|| keyCode == KeyEvent.KEYCODE_MEDIA_STOP
|| keyCode == KeyEvent.KEYCODE_MEDIA_NEXT
|| keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
CastWebContentsComponent.onKeyDown(this, mSurfaceHelper.getInstanceId(), keyCode);
CastWebContentsComponent.onKeyDown(mSurfaceHelper.getInstanceId(), keyCode);
// Stop key should end the entire session.
if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {
......
......@@ -38,6 +38,10 @@ public class CastWebContentsFragment extends Fragment {
private View mFragmentRootView;
private String mAppId;
private int mInitialVisiblityPriority;
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
......@@ -83,20 +87,27 @@ public class CastWebContentsFragment extends Fragment {
(FrameLayout) getView().findViewById(R.id.web_contents_container),
true /* showInFragment */);
Bundle bundle = getArguments();
bundle.setClassLoader(WebContents.class.getClassLoader());
String uriString = bundle.getString(CastWebContentsComponent.INTENT_EXTRA_URI);
String uriString = CastWebContentsIntentUtils.getUriString(bundle);
if (uriString == null) {
return;
}
Uri uri = Uri.parse(uriString);
WebContents webContents = (WebContents) bundle.getParcelable(
CastWebContentsComponent.ACTION_EXTRA_WEB_CONTENTS);
boolean touchInputEnabled =
bundle.getBoolean(CastWebContentsComponent.ACTION_EXTRA_TOUCH_INPUT_ENABLED, false);
WebContents webContents = CastWebContentsIntentUtils.getWebContents(bundle);
mAppId = CastWebContentsIntentUtils.getAppId(bundle);
mInitialVisiblityPriority = CastWebContentsIntentUtils.getVisibilityPriority(bundle);
boolean touchInputEnabled = CastWebContentsIntentUtils.isTouchable(bundle);
mSurfaceHelper.onNewWebContents(uri, webContents, touchInputEnabled);
}
@Override
public void setArguments(Bundle args) {
super.setArguments(args);
args.setClassLoader(WebContents.class.getClassLoader());
}
@Override
public void onPause() {
Log.d(TAG, "onPause");
......
......@@ -44,8 +44,7 @@ public class CastWebContentsService extends Service {
intent.setExtrasClassLoader(WebContents.class.getClassLoader());
mInstanceId = intent.getData().getPath();
WebContents webContents = (WebContents) intent.getParcelableExtra(
CastWebContentsComponent.ACTION_EXTRA_WEB_CONTENTS);
WebContents webContents = CastWebContentsIntentUtils.getWebContents(intent);
if (webContents == null) {
Log.e(TAG, "Received null WebContents in intent.");
return;
......@@ -117,7 +116,7 @@ public class CastWebContentsService extends Service {
mContentViewCore = null;
// Inform CastContentWindowAndroid we're detaching.
CastWebContentsComponent.onComponentClosed(this, mInstanceId);
CastWebContentsComponent.onComponentClosed(mInstanceId);
}
}
}
......@@ -12,10 +12,8 @@ import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.FrameLayout;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chromecast.base.CastSwitches;
......@@ -92,15 +90,16 @@ class CastWebContentsSurfaceHelper {
maybeFinishLater();
});
});
// Receive broadcasts requesting to tear down this app while we have a valid URI.
mHasUriState.watch((Uri uri) -> {
IntentFilter filter = new IntentFilter();
filter.addAction(CastIntents.ACTION_STOP_WEB_CONTENT);
return new LocalBroadcastReceiverScope(filter, (Intent intent) -> {
String intentUri = intent.getStringExtra(CastWebContentsComponent.INTENT_EXTRA_URI);
String intentUri = CastWebContentsIntentUtils.getUriString(intent);
Log.d(TAG, "Intent action=" + intent.getAction() + "; URI=" + intentUri);
if (!uri.toString().equals(intentUri)) {
Log.d(TAG, "Current URI=" + uri + "; intent URI=" + intentUri);
Log.d(TAG, "Current URI=" + mUri + "; intent URI=" + intentUri);
return;
}
detachWebContentsIfAny();
......@@ -136,19 +135,16 @@ class CastWebContentsSurfaceHelper {
// Closes this activity if a new WebContents is not being displayed.
private void maybeFinishLater() {
Log.d(TAG, "maybeFinishLater");
Log.d(TAG, "maybeFinishLater: " + mUri);
final String currentInstanceId = mInstanceId;
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (currentInstanceId != null && currentInstanceId.equals(mInstanceId)) {
if (mShowInFragment) {
Intent in = new Intent();
in.setAction(CastIntents.ACTION_ON_WEB_CONTENT_STOPPED);
in.putExtra(CastWebContentsComponent.INTENT_EXTRA_URI, mUri.toString());
Log.d(TAG, "Sending intent: ON_WEB_CONTENT_STOPPED: URI=" + mUri);
LocalBroadcastManager.getInstance(ContextUtils.getApplicationContext())
.sendBroadcastSync(in);
CastWebContentsIntentUtils.getLocalBroadcastManager().sendBroadcastSync(
CastWebContentsIntentUtils.onWebContentStopped(mUri));
} else {
Log.d(TAG, "Finishing cast content activity of URI:" + mUri);
mHostActivity.finish();
......@@ -164,7 +160,7 @@ class CastWebContentsSurfaceHelper {
// Sets webContents to be the currently displayed webContents.
private void showWebContents(WebContents webContents) {
Log.d(TAG, "showWebContents");
Log.d(TAG, "showWebContents: " + mUri);
detachWebContentsIfAny();
......@@ -206,7 +202,7 @@ class CastWebContentsSurfaceHelper {
// Remove the currently displayed webContents. no-op if nothing is being displayed.
void detachWebContentsIfAny() {
Log.d(TAG, "Detach web contents if any.");
Log.d(TAG, "Maybe detach web contents if any: " + mUri);
if (mContentView != null) {
mCastWebContentsLayout.removeView(mContentView);
mCastWebContentsLayout.removeView(mContentViewRenderView);
......@@ -217,12 +213,13 @@ class CastWebContentsSurfaceHelper {
mContentViewCore = null;
mContentViewRenderView = null;
mWindow = null;
CastWebContentsComponent.onComponentClosed(getActivity(), mInstanceId);
Log.d(TAG, "Detach web contents done.");
CastWebContentsComponent.onComponentClosed(mInstanceId);
Log.d(TAG, "Detach web contents done: " + mUri);
}
}
void onPause() {
Log.d(TAG, "onPause: " + mUri);
mResumedState.reset();
if (mContentViewCore != null) {
......@@ -251,6 +248,7 @@ class CastWebContentsSurfaceHelper {
}
void onResume() {
Log.d(TAG, "onResume: " + mUri);
mResumedState.set(Unit.unit());
if (mContentViewCore != null) {
......@@ -260,6 +258,7 @@ class CastWebContentsSurfaceHelper {
// Destroys all resources. After calling this method, this object must be dropped.
void onDestroy() {
Log.d(TAG, "onDestroy: " + mUri);
detachWebContentsIfAny();
mHasWebContentsState.reset();
mHasUriState.reset();
......
......@@ -7,12 +7,17 @@
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/memory/ptr_util.h"
#include "content/public/browser/web_contents.h"
#include "jni/CastContentWindowAndroid_jni.h"
#include "ui/events/keycodes/keyboard_code_conversion_android.h"
namespace chromecast {
using base::android::ConvertUTF8ToJavaString;
namespace shell {
namespace {
......@@ -56,14 +61,15 @@ CastContentWindowAndroid::~CastContentWindowAndroid() {
void CastContentWindowAndroid::CreateWindowForWebContents(
content::WebContents* web_contents,
CastWindowManager* /* window_manager */,
bool /* is_visible */) {
bool /* is_visible */,
VisibilityPriority visibility_priority) {
DCHECK(web_contents);
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
web_contents->GetJavaWebContents();
Java_CastContentWindowAndroid_createWindowForWebContents(env, java_window_,
java_web_contents);
Java_CastContentWindowAndroid_createWindowForWebContents(
env, java_window_, java_web_contents, static_cast<int>(visibility_priority));
}
void CastContentWindowAndroid::OnActivityStopped(
......@@ -82,5 +88,37 @@ void CastContentWindowAndroid::OnKeyDown(
delegate_->OnKeyEvent(key_event);
}
void CastContentWindowAndroid::RequestVisibility(
VisibilityPriority visibility_priority) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CastContentWindowAndroid_requestVisibilityPriority(env, java_window_,
static_cast<int>(visibility_priority));
}
void CastContentWindowAndroid::RequestMoveOut() {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CastContentWindowAndroid_requestMoveOut(env, java_window_);
}
bool CastContentWindowAndroid::ConsumeGesture(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
int gesture_type) {
return delegate_->ConsumeGesture(static_cast<GestureType>(gesture_type));
}
void CastContentWindowAndroid::OnVisibilityChange(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
int visibility_type) {
delegate_->OnVisibilityChange(static_cast<VisibilityType>(visibility_type));
}
base::android::ScopedJavaLocalRef<jstring> CastContentWindowAndroid::GetId(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
return ConvertUTF8ToJavaString(env, delegate_->GetId());
}
} // namespace shell
} // namespace chromecast
......@@ -25,9 +25,15 @@ class CastContentWindowAndroid : public CastContentWindow {
~CastContentWindowAndroid() override;
// CastContentWindow implementation:
void CreateWindowForWebContents(content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible) override;
void CreateWindowForWebContents(
content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible,
VisibilityPriority visibility_priority) override;
void RequestVisibility(VisibilityPriority visibility_priority) override;
void RequestMoveOut() override;
// Called through JNI.
void OnActivityStopped(JNIEnv* env,
......@@ -35,6 +41,15 @@ class CastContentWindowAndroid : public CastContentWindow {
void OnKeyDown(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
int keycode);
bool ConsumeGesture(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
int gesture_type);
void OnVisibilityChange(JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
int visibility_type);
base::android::ScopedJavaLocalRef<jstring> GetId(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller);
private:
friend class CastContentWindow;
......
......@@ -35,6 +35,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import org.chromium.base.ContextUtils;
import org.chromium.chromecast.shell.CastWebContentsComponent.StartParams;
import org.chromium.content_public.browser.WebContents;
import org.chromium.testing.local.LocalRobolectricTestRunner;
......@@ -54,17 +55,23 @@ public class CastWebContentsComponentTest {
private static final String INSTANCE_ID = "1";
private static final String APP_ID = "app";
private static final int VISIBILITY_PRIORITY = 2;
@Mock
private WebContents mWebContents;
private Activity mActivity;
private ShadowActivity mShadowActivity;
private StartParams mStartParams;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Mockito.spy(Robolectric.buildActivity(Activity.class).setup().get());
mShadowActivity = Shadows.shadowOf(mActivity);
mStartParams = new StartParams(mActivity, mWebContents, APP_ID, VISIBILITY_PRIORITY);
}
@Test
......@@ -72,8 +79,8 @@ public class CastWebContentsComponentTest {
Assume.assumeFalse(BuildConfig.DISPLAY_WEB_CONTENTS_IN_SERVICE);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
component.start(mActivity, mWebContents);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
component.start(mStartParams);
Intent intent = mShadowActivity.getNextStartedActivity();
Assert.assertEquals(
intent.getComponent().getClassName(), CastWebContentsActivity.class.getName());
......@@ -91,8 +98,8 @@ public class CastWebContentsComponentTest {
.registerReceiver(receiver, intentFilter);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
component.start(ContextUtils.getApplicationContext(), mWebContents);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
component.start(mStartParams);
component.stop(ContextUtils.getApplicationContext());
LocalBroadcastManager.getInstance(ContextUtils.getApplicationContext())
......@@ -106,8 +113,8 @@ public class CastWebContentsComponentTest {
Assume.assumeTrue(BuildConfig.DISPLAY_WEB_CONTENTS_IN_SERVICE);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
component.start(mActivity, mWebContents);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
component.start(mStartParams);
component.stop(mActivity);
ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
......@@ -122,8 +129,8 @@ public class CastWebContentsComponentTest {
Assume.assumeTrue(BuildConfig.DISPLAY_WEB_CONTENTS_IN_SERVICE);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
component.start(mActivity, mWebContents);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
component.start(mStartParams);
component.stop(mActivity);
verify(mActivity).unbindService(any(ServiceConnection.class));
......@@ -135,10 +142,9 @@ public class CastWebContentsComponentTest {
Mockito.mock(CastWebContentsComponent.OnComponentClosedHandler.class);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, callback, null, false, false);
component.start(ContextUtils.getApplicationContext(), mWebContents);
CastWebContentsComponent.onComponentClosed(
ContextUtils.getApplicationContext(), INSTANCE_ID);
new CastWebContentsComponent(INSTANCE_ID, callback, null, null, false, false);
component.start(mStartParams);
CastWebContentsComponent.onComponentClosed(INSTANCE_ID);
verify(callback).onComponentClosed();
component.stop(mActivity);
......@@ -150,9 +156,9 @@ public class CastWebContentsComponentTest {
Mockito.mock(CastWebContentsComponent.OnKeyDownHandler.class);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, callback, false, false);
component.start(mActivity, mWebContents);
CastWebContentsComponent.onKeyDown(mActivity, INSTANCE_ID, 42);
new CastWebContentsComponent(INSTANCE_ID, null, callback, null, false, false);
component.start(mStartParams);
CastWebContentsComponent.onKeyDown(INSTANCE_ID, 42);
component.stop(mActivity);
verify(callback).onKeyDown(42);
......@@ -161,29 +167,57 @@ public class CastWebContentsComponentTest {
@Test
public void testStopDoesNotUnbindServiceIfStartWasNotCalled() {
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
component.stop(mActivity);
verify(mActivity, never()).unbindService(any(ServiceConnection.class));
}
@Test
public void testOnVisibilityChangeCallback() {
CastWebContentsComponent.SurfaceEventHandler callback =
Mockito.mock(CastWebContentsComponent.SurfaceEventHandler.class);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, callback, false, false);
component.start(mStartParams);
CastWebContentsComponent.onVisiblityChange(INSTANCE_ID, 2);
component.stop(mActivity);
verify(callback).onVisibilityChange(2);
}
@Test
public void testOnGestureCallback() {
CastWebContentsComponent.SurfaceEventHandler callback =
Mockito.mock(CastWebContentsComponent.SurfaceEventHandler.class);
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, callback, false, false);
component.start(mStartParams);
CastWebContentsComponent.onGesture(INSTANCE_ID, 1);
component.stop(mActivity);
verify(callback).consumeGesture(1);
}
@Test
public void testStartWebContentsComponentMultipleTimes() {
CastWebContentsComponent component =
new CastWebContentsComponent(INSTANCE_ID, null, null, false, false);
new CastWebContentsComponent(INSTANCE_ID, null, null, null, false, false);
CastWebContentsComponent.Delegate delegate = mock(CastWebContentsComponent.Delegate.class);
component.setDelegate(delegate);
component.start(mActivity, mWebContents);
Object receiver1 = component.getIntentReceiver();
Assert.assertNotNull(receiver1);
verify(delegate, times(1)).start(eq(mActivity), eq(mWebContents));
component.start(mActivity, mWebContents);
Object receiver2 = component.getIntentReceiver();
Assert.assertEquals(receiver1, receiver2);
verify(delegate, times(1)).start(any(Context.class), any(WebContents.class));
component.start(mStartParams);
Assert.assertTrue(component.isStarted());
verify(delegate, times(1)).start(eq(mStartParams));
StartParams params2 = new StartParams(mActivity, mWebContents, "test", 1);
component.start(params2);
Assert.assertTrue(component.isStarted());
verify(delegate, times(1)).start(any(StartParams.class));
verify(delegate, times(0)).start(eq(params2));
component.stop(mActivity);
Assert.assertNull(component.getIntentReceiver());
Assert.assertFalse(component.isStarted());
verify(delegate, times(1)).stop(any(Context.class));
}
}
// Copyright 2018 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.app.Activity;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.chromium.base.ContextUtils;
import org.chromium.content_public.browser.WebContents;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Tests for CastWebContentsComponent.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE, application = CastWebContentsComponentTest.FakeApplication.class)
public class CastWebContentsIntentUtilsTest {
public static class FakeApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
ContextUtils.initApplicationContextForTests(this);
}
}
private static final String INSTANCE_ID = "1";
private static final String EXPECTED_URI = "cast://webcontents/1";
private static final String APP_ID = "app";
private static final int VISIBILITY_PRIORITY = 2;
@Mock
private WebContents mWebContents;
@Mock
private BroadcastReceiver mReceiver;
private Activity mActivity;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Mockito.spy(Robolectric.buildActivity(Activity.class).setup().get());
}
@Test
public void testOnActivityStopped() {
Intent in = CastWebContentsIntentUtils.onActivityStopped(INSTANCE_ID);
String uri = in.getDataString();
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentOfActivityStopped(in));
}
@Test
public void testOnGesture() {
Intent in = CastWebContentsIntentUtils.onGesture(INSTANCE_ID, 1);
String uri = in.getDataString();
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
int type = CastWebContentsIntentUtils.getGestureType(in);
Assert.assertEquals(1, type);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentOfGesturing(in));
}
@Test
public void testOnKeyDown() {
Intent in = CastWebContentsIntentUtils.onKeyDown(INSTANCE_ID, 32);
String uri = in.getDataString();
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
int type = CastWebContentsIntentUtils.getKeyCode(in);
Assert.assertEquals(32, type);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentOfKeyEvent(in));
}
@Test
public void testOnVisibilityChange() {
Intent in = CastWebContentsIntentUtils.onVisiblityChange(INSTANCE_ID, 3);
String uri = in.getDataString();
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
int type = CastWebContentsIntentUtils.getVisibilityType(in);
Assert.assertEquals(3, type);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentOfVisiblityChange(in));
}
@Test
public void testRequestVisibilityPriority() {
Intent in = CastWebContentsIntentUtils.requestVisibilityPriority(INSTANCE_ID, 2);
Assert.assertNull(in.getData());
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
int type = CastWebContentsIntentUtils.getVisibilityPriority(in);
Assert.assertEquals(2, type);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentToRequestVisibilityPriority(in));
}
@Test
public void testRequestMoveOut() {
Intent in = CastWebContentsIntentUtils.requestMoveOut(INSTANCE_ID);
Assert.assertNull(in.getData());
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
Assert.assertTrue(CastWebContentsIntentUtils.isIntentToRequestMoveOut(in));
}
@Test
public void testGestureConsumed() {
Intent in = CastWebContentsIntentUtils.gestureConsumed(INSTANCE_ID, 1, true);
Assert.assertNull(in.getData());
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertEquals(EXPECTED_URI, uri);
int type = CastWebContentsIntentUtils.getGestureType(in);
Assert.assertEquals(1, type);
Assert.assertTrue(CastWebContentsIntentUtils.isGestureConsumed(in));
Assert.assertEquals(CastWebContentsIntentUtils.ACTION_GESTURE_CONSUMED,
in.getAction());
in = CastWebContentsIntentUtils.gestureConsumed(INSTANCE_ID, 2, false);
Assert.assertNull(in.getData());
uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
type = CastWebContentsIntentUtils.getGestureType(in);
Assert.assertEquals(2, type);
Assert.assertFalse(CastWebContentsIntentUtils.isGestureConsumed(in));
}
@Test
public void testRequestStartCastActivity() {
Intent in = CastWebContentsIntentUtils.requestStartCastActivity(
mActivity, mWebContents, true, INSTANCE_ID);
Assert.assertNull(in.getData());
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
WebContents webContents = CastWebContentsIntentUtils.getWebContents(in);
Assert.assertEquals(mWebContents, webContents);
Assert.assertTrue(CastWebContentsIntentUtils.isTouchable(in));
Assert.assertEquals(Intent.ACTION_VIEW, in.getAction());
}
@Test
public void testRequestStartCastFragment() {
Intent in = CastWebContentsIntentUtils.requestStartCastFragment(
mWebContents, APP_ID, 3, true, INSTANCE_ID);
Assert.assertNull(in.getData());
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
WebContents webContents = CastWebContentsIntentUtils.getWebContents(in);
Assert.assertEquals(mWebContents, webContents);
Assert.assertTrue(CastWebContentsIntentUtils.isTouchable(in));
Assert.assertEquals(APP_ID, CastWebContentsIntentUtils.getAppId(in));
Assert.assertEquals(3, CastWebContentsIntentUtils.getVisibilityPriority(in));
Assert.assertEquals(CastIntents.ACTION_SHOW_WEB_CONTENT, in.getAction());
}
@Test
public void testRequestStartCastService() {
Intent in = CastWebContentsIntentUtils.requestStartCastService(
mActivity, mWebContents, INSTANCE_ID);
String uri = in.getDataString();
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
WebContents webContents = CastWebContentsIntentUtils.getWebContents(in);
Assert.assertEquals(mWebContents, webContents);
Assert.assertEquals(Intent.ACTION_VIEW, in.getAction());
}
@Test
public void testRequestStopWebContents() {
Intent in = CastWebContentsIntentUtils.requestStopWebContents(INSTANCE_ID);
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
}
@Test
public void testOnWebContentStopped() {
Intent in = CastWebContentsIntentUtils.onWebContentStopped(Uri.parse(EXPECTED_URI));
String uri = CastWebContentsIntentUtils.getUriString(in);
Assert.assertNotNull(uri);
Assert.assertEquals(EXPECTED_URI, uri);
}
}
......@@ -21,6 +21,30 @@ class CastWindowManager;
namespace shell {
enum class VisibilityType {
UNKNOWN = 0,
FULL_SCREEN = 1,
PARTIAL_OUT = 2,
HIDDEN = 3
};
enum class VisibilityPriority {
// Default priority, up to system to decide how to show the app.
DEFAULT = 0,
// Priority for app need to show in full screen mode but could be timout.
TRANSIENT_ACTIVITY = 1,
// A high priority interruption takes half screen if a sticky activity
// showing on screen, otherwise takes full screen.
HIGH_PRIORITY_INTERRUPTION = 2,
// Priority for app need to show in full screen mode and stick to screen.
STICKY_ACTIVITY = 3,
};
enum class GestureType { NO_GESTURE = 0, GO_BACK = 1 };
// Class that represents the "window" a WebContents is displayed in cast_shell.
// For Linux, this represents an Aura window. For Android, this is a Activity.
// See CastContentWindowLinux and CastContentWindowAndroid.
......@@ -31,6 +55,17 @@ class CastContentWindow {
virtual void OnWindowDestroyed() = 0;
virtual void OnKeyEvent(const ui::KeyEvent& key_event) = 0;
// To be called from Android side through JNI to send surface gesture to
// cast activity or appliction.
virtual bool ConsumeGesture(GestureType gesture_type) = 0;
// To be called from Android side through JNI to notify cast activity or
// appliction its visibility change in Android app hosting it.
virtual void OnVisibilityChange(VisibilityType visibility_type) = 0;
// Returns app ID of cast activity or appliction.
virtual std::string GetId() = 0;
protected:
virtual ~Delegate() {}
};
......@@ -48,9 +83,19 @@ class CastContentWindow {
// |is_visible| is true.
// |web_contents| should outlive this CastContentWindow.
// |window_manager| should outlive this CastContentWindow.
virtual void CreateWindowForWebContents(content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible) = 0;
virtual void CreateWindowForWebContents(
content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible,
VisibilityPriority visibility_priority) = 0;
// Cast activity or application calls it to request for a visibility priority
// change.
virtual void RequestVisibility(VisibilityPriority visibility_priority) = 0;
// Cast activity or application calls it to request for moving out of the
// screen.
virtual void RequestMoveOut() = 0;
};
} // namespace shell
......
......@@ -57,7 +57,8 @@ CastContentWindowAura::CastContentWindowAura(bool is_touch_enabled)
void CastContentWindowAura::CreateWindowForWebContents(
content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible) {
bool is_visible,
VisibilityPriority visibility_priority) {
DCHECK(web_contents);
DCHECK(window_manager);
gfx::NativeView window = web_contents->GetNativeView();
......@@ -75,5 +76,10 @@ void CastContentWindowAura::CreateWindowForWebContents(
}
}
void CastContentWindowAura::RequestVisibility(
VisibilityPriority visibility_priority){};
void CastContentWindowAura::RequestMoveOut(){};
} // namespace shell
} // namespace chromecast
......@@ -22,9 +22,13 @@ class CastContentWindowAura : public CastContentWindow {
~CastContentWindowAura() override;
// CastContentWindow implementation.
void CreateWindowForWebContents(content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible) override;
void CreateWindowForWebContents(
content::WebContents* web_contents,
CastWindowManager* window_manager,
bool is_visible,
VisibilityPriority visibility_priority) override;
void RequestVisibility(VisibilityPriority visibility_priority) override;
void RequestMoveOut() override;
private:
friend class CastContentWindow;
......
......@@ -134,8 +134,9 @@ void CastWebViewDefault::CreateWindow(CastWindowManager* window_manager,
}
DCHECK(window_manager);
window_->CreateWindowForWebContents(web_contents_.get(), window_manager,
is_visible);
window_->CreateWindowForWebContents(
web_contents_.get(), window_manager, is_visible,
chromecast::shell::VisibilityPriority::STICKY_ACTIVITY);
web_contents_->Focus();
}
......
......@@ -63,7 +63,7 @@ void CastWebViewExtension::ClosePage(const base::TimeDelta& shutdown_delay) {}
void CastWebViewExtension::CreateWindow(CastWindowManager* window_manager,
bool is_visible) {
window_->CreateWindowForWebContents(web_contents(), window_manager,
is_visible);
is_visible, chromecast::shell::VisibilityPriority::DEFAULT);
web_contents()->Focus();
}
......
......@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/time/time.h"
#include "chromecast/browser/cast_content_window.h"
#include "chromecast/browser/cast_web_contents_manager.h"
#include "chromecast/browser/cast_web_view_factory.h"
#include "content/public/browser/web_contents.h"
......@@ -99,5 +100,15 @@ bool CastServiceSimple::OnAddMessageToConsoleReceived(
return false;
}
bool CastServiceSimple::ConsumeGesture(GestureType gesture_type) {
return false;
};
void CastServiceSimple::OnVisibilityChange(VisibilityType visibility_type) {}
std::string CastServiceSimple::GetId() {
return "";
}
} // namespace shell
} // namespace chromecast
......@@ -8,6 +8,7 @@
#include <memory>
#include "base/macros.h"
#include "chromecast/browser/cast_content_window.h"
#include "chromecast/browser/cast_web_view.h"
#include "chromecast/service/cast_service.h"
#include "url/gurl.h"
......@@ -46,6 +47,9 @@ class CastServiceSimple : public CastService, public CastWebView::Delegate {
// CastContentWindow::Delegate implementation:
void OnWindowDestroyed() override;
void OnKeyEvent(const ui::KeyEvent& key_event) override;
bool ConsumeGesture(GestureType gesture_type) override;
void OnVisibilityChange(VisibilityType visibility_type) override;
std::string GetId() override;
private:
CastWindowManager* const window_manager_;
......
......@@ -90,6 +90,16 @@ void CastBrowserTest::OnWindowDestroyed() {}
void CastBrowserTest::OnKeyEvent(const ui::KeyEvent& key_event) {}
void CastBrowserTest::OnVisibilityChange(VisibilityType visibility_type) {}
bool CastBrowserTest::ConsumeGesture(GestureType gesture_type) {
return false;
};
std::string CastBrowserTest::GetId() {
return "";
}
bool CastBrowserTest::OnAddMessageToConsoleReceived(
content::WebContents* source,
int32_t level,
......
......@@ -53,6 +53,9 @@ class CastBrowserTest : public content::BrowserTestBase, CastWebView::Delegate {
const base::string16& message,
int32_t line_no,
const base::string16& source_id) override;
void OnVisibilityChange(VisibilityType visibility_type) override;
bool ConsumeGesture(GestureType gesture_type) override;
std::string GetId() override;
std::unique_ptr<CastWebViewFactory> web_view_factory_;
std::unique_ptr<CastWebContentsManager> web_contents_manager_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment