Commit 3e1e45d5 authored by ltian's avatar ltian Committed by Commit bot

[Android] Add a transparent Activity in Chrome to handle Browser Action Intent

Add a transparent Activity in Chrome to handle the Browser Action
Intent. This CL also deals with parsing the information of Browser
Action request from the Intent. Later, this Activity will open a
context menu from Chrome with the request information.

BUG=706696

Review-Url: https://codereview.chromium.org/2786283002
Cr-Commit-Position: refs/heads/master@{#467837}
parent ca74aee3
...@@ -502,7 +502,7 @@ deps_os = { ...@@ -502,7 +502,7 @@ deps_os = {
Var('chromium_git') + '/external/github.com/kennethreitz/requests.git' + '@' + 'f172b30356d821d180fa4ecfa3e71c7274a32de4', Var('chromium_git') + '/external/github.com/kennethreitz/requests.git' + '@' + 'f172b30356d821d180fa4ecfa3e71c7274a32de4',
'src/third_party/custom_tabs_client/src': 'src/third_party/custom_tabs_client/src':
Var('chromium_git') + '/external/github.com/GoogleChrome/custom-tabs-client.git' + '@' + '3c95e2dda9b292653ff13454f093e5ff136814d2', Var('chromium_git') + '/external/github.com/GoogleChrome/custom-tabs-client.git' + '@' + '4889dd9d552d24f08584dde29f639c0da4ea0f12',
'src/third_party/gvr-android-sdk/src': 'src/third_party/gvr-android-sdk/src':
Var('chromium_git') + '/external/github.com/googlevr/gvr-android-sdk.git' + '@' + '8d1395957283ee13ebe2bc672ba24e5ca4ec343f', Var('chromium_git') + '/external/github.com/googlevr/gvr-android-sdk.git' + '@' + '8d1395957283ee13ebe2bc672ba24e5ca4ec343f',
......
...@@ -372,6 +372,7 @@ junit_binary("chrome_junit_tests") { ...@@ -372,6 +372,7 @@ junit_binary("chrome_junit_tests") {
"//third_party/android_tools:android_support_v7_mediarouter_java", "//third_party/android_tools:android_support_v7_mediarouter_java",
"//third_party/android_tools:android_support_v7_recyclerview_java", "//third_party/android_tools:android_support_v7_recyclerview_java",
"//third_party/cacheinvalidation:cacheinvalidation_javalib", "//third_party/cacheinvalidation:cacheinvalidation_javalib",
"//third_party/custom_tabs_client:custom_tabs_support_java",
"//third_party/hamcrest:hamcrest_java", "//third_party/hamcrest:hamcrest_java",
"//ui/android:ui_java", "//ui/android:ui_java",
"//url/mojo:url_mojom_gurl_java", "//url/mojo:url_mojom_gurl_java",
......
...@@ -605,6 +605,21 @@ by a child template that "extends" this file. ...@@ -605,6 +605,21 @@ by a child template that "extends" this file.
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" > android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" >
</activity> </activity>
<!-- Activities for Browser Actions -->
{% if channel in ['default'] %}
<activity android:name="org.chromium.chrome.browser.browseractions.BrowserActionActivity"
android:theme="@style/FullscreenTransparentActivityTheme"
android:exported="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize">
<intent-filter>
<action android:name="android.support.customtabs.browseractions.browser_action_open" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
</activity>
{% endif %}
<!-- Service for Broadcasting a Physical Web URL --> <!-- Service for Broadcasting a Physical Web URL -->
<service <service
android:name="org.chromium.chrome.browser.physicalweb.PhysicalWebBroadcastService" android:name="org.chromium.chrome.browser.physicalweb.PhysicalWebBroadcastService"
......
...@@ -59,6 +59,15 @@ ...@@ -59,6 +59,15 @@
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
</style> </style>
<style name="FullscreenTransparentActivityTheme" parent="Theme.AppCompat.Light.NoActionBar" >
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="windowNoTitle">true</item>
</style>
<style name="FullscreenWhiteActivityTheme" parent="FullscreenWhite"> <style name="FullscreenWhiteActivityTheme" parent="FullscreenWhite">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
</style> </style>
......
// Copyright 2017 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.chrome.browser.browseractions;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.browseractions.BrowserActionItem;
import android.support.customtabs.browseractions.BrowserActionsIntent;
import org.chromium.base.Log;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.init.AsyncInitializationActivity;
import org.chromium.chrome.browser.util.IntentUtils;
import java.util.ArrayList;
import java.util.List;
/**
* A transparent {@link AsyncInitializationActivity} that displays the browser action context menu.
*/
public class BrowserActionActivity extends AsyncInitializationActivity {
private static final String TAG = "BrowserActions";
private int mType;
private Uri mUri;
private String mCreatorPackageName;
private List<BrowserActionItem> mActions = new ArrayList<>();
@Override
protected void setContentView() {
openContextMenu();
}
@Override
@SuppressFBWarnings("URF_UNREAD_FIELD")
protected boolean isStartedUpCorrectly(Intent intent) {
if (intent == null
|| !BrowserActionsIntent.ACTION_BROWSER_ACTIONS_OPEN.equals(intent.getAction())) {
return false;
}
mUri = Uri.parse(IntentHandler.getUrlFromIntent(intent));
mType = IntentUtils.safeGetIntExtra(
intent, BrowserActionsIntent.EXTRA_TYPE, BrowserActionsIntent.URL_TYPE_NONE);
mCreatorPackageName = BrowserActionsIntent.getCreatorPackageName(intent);
ArrayList<Bundle> bundles = IntentUtils.getParcelableArrayListExtra(
intent, BrowserActionsIntent.EXTRA_MENU_ITEMS);
if (bundles != null) {
parseBrowserActionItems(bundles);
}
if (mUri == null) {
Log.e(TAG, "Missing url");
return false;
} else if (!UrlConstants.HTTP_SCHEME.equals(mUri.getScheme())
&& !UrlConstants.HTTPS_SCHEME.equals(mUri.getScheme())) {
Log.e(TAG, "Url should only be HTTP or HTTPS scheme");
return false;
} else if (mCreatorPackageName == null) {
Log.e(TAG, "Missing creator's pacakge name");
return false;
} else if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
Log.e(TAG, "Intent should not be started with FLAG_ACTIVITY_NEW_TASK");
return false;
} else if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
Log.e(TAG, "Intent should not be started with FLAG_ACTIVITY_NEW_DOCUMENT");
return false;
} else {
return true;
}
}
/**
* Opens a Browser Actions context menu based on the parsed data.
*/
public void openContextMenu() {
return;
}
@Override
protected boolean shouldDelayBrowserStartup() {
return true;
}
@Override
public boolean shouldStartGpuProcess() {
return true;
}
/**
* Gets custom item list for browser action menu.
* @param bundles Data for custom items from {@link BrowserActionsIntent}.
*/
private void parseBrowserActionItems(ArrayList<Bundle> bundles) {
for (int i = 0; i < bundles.size(); i++) {
Bundle bundle = bundles.get(i);
String title = IntentUtils.safeGetString(bundle, BrowserActionsIntent.KEY_TITLE);
PendingIntent action =
IntentUtils.safeGetParcelable(bundle, BrowserActionsIntent.KEY_ACTION);
Bitmap icon = IntentUtils.safeGetParcelable(bundle, BrowserActionsIntent.KEY_ICON);
if (title != null && action != null) {
BrowserActionItem item = new BrowserActionItem(title, action);
if (icon != null) {
item.setIcon(icon);
}
mActions.add(item);
} else if (title != null) {
Log.e(TAG, "Missing action for item: " + i);
} else if (action != null) {
Log.e(TAG, "Missing title for item: " + i);
} else {
Log.e(TAG, "Missing title and action for item: " + i);
}
}
}
/**
* Callback when Browser Actions menu dialog is shown.
*/
private void onMenuShown() {
beginLoadingLibrary();
}
}
...@@ -120,6 +120,7 @@ chrome_java_sources = [ ...@@ -120,6 +120,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java",
"java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProxy.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProxy.java",
"java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetService.java",
"java/src/org/chromium/chrome/browser/browseractions/BrowserActionActivity.java",
"java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java", "java/src/org/chromium/chrome/browser/browsing_data/UrlFilters.java",
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountFeedbackReporter.java",
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java", "java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
...@@ -1635,6 +1636,7 @@ chrome_junit_test_java_sources = [ ...@@ -1635,6 +1636,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/DisableHistogramsRule.java", "junit/src/org/chromium/chrome/browser/DisableHistogramsRule.java",
"junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java", "junit/src/org/chromium/chrome/browser/ShortcutHelperTest.java",
"junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java", "junit/src/org/chromium/chrome/browser/SSLClientCertificateRequestTest.java",
"junit/src/org/chromium/chrome/browser/browseractions/BrowserActionActivityTest.java",
"junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java", "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java",
"junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java", "junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerTest.java",
"junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java", "junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java",
......
// Copyright 2017 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.chrome.browser.browseractions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doAnswer;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.customtabs.browseractions.BrowserActionsIntent;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.chromium.base.test.util.Feature;
import org.chromium.testing.local.LocalRobolectricTestRunner;
/**
* Unit tests for BrowserActionActivity.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class BrowserActionActivityTest {
private static final String HTTP_SCHEME_TEST_URL = "http://www.example.com";
private static final String HTTPS_SCHEME_TEST_URL = "https://www.example.com";
private static final String CHROME_SCHEME_TEST_URL = "chrome://example";
private static final String CONTENT_SCHEME_TEST_URL = "content://example";
private BrowserActionActivity mActivity = new BrowserActionActivity();
private Context mContext;
@Mock
private PendingIntent mPendingIntent;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
Answer<String> answer = new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) {
return "some.other.app.package.name";
}
};
doAnswer(answer).when(mPendingIntent).getCreatorPackage();
}
@Test
@Feature({"BrowserActions"})
public void testStartedUpCorrectly() {
assertFalse(mActivity.isStartedUpCorrectly(null));
assertFalse(mActivity.isStartedUpCorrectly(new Intent()));
Intent mIntent = createBaseBrowserActionsIntent(HTTP_SCHEME_TEST_URL);
assertTrue(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(HTTP_SCHEME_TEST_URL);
mIntent.removeExtra(BrowserActionsIntent.EXTRA_APP_ID);
assertFalse(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(HTTPS_SCHEME_TEST_URL);
assertTrue(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(CHROME_SCHEME_TEST_URL);
assertFalse(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(CONTENT_SCHEME_TEST_URL);
assertFalse(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(HTTP_SCHEME_TEST_URL);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
assertFalse(mActivity.isStartedUpCorrectly(mIntent));
mIntent = createBaseBrowserActionsIntent(HTTP_SCHEME_TEST_URL);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
assertFalse(mActivity.isStartedUpCorrectly(mIntent));
}
/**
* Creates a simple Intent for Browser Actions which contains a url, the {@link
* BrowserActionsIntent.ACTION_BROWSER_ACTIONS_OPEN} action and source package name.
* @param url The url for the data of the Intent.
* @return The simple Intent for Browser Actions.
*/
private Intent createBaseBrowserActionsIntent(String url) {
return new BrowserActionsIntent.Builder(mContext, Uri.parse(url))
.build()
.getIntent()
.putExtra(BrowserActionsIntent.EXTRA_APP_ID, mPendingIntent);
}
}
...@@ -43,6 +43,8 @@ android_library("custom_tabs_client_shared_java") { ...@@ -43,6 +43,8 @@ android_library("custom_tabs_client_shared_java") {
android_library("custom_tabs_support_java") { android_library("custom_tabs_support_java") {
java_files = [ java_files = [
"src/customtabs/src/android/support/customtabs/browseractions/BrowserActionsIntent.java",
"src/customtabs/src/android/support/customtabs/browseractions/BrowserActionItem.java",
"src/customtabs/src/android/support/customtabs/CustomTabsCallback.java", "src/customtabs/src/android/support/customtabs/CustomTabsCallback.java",
"src/customtabs/src/android/support/customtabs/CustomTabsClient.java", "src/customtabs/src/android/support/customtabs/CustomTabsClient.java",
"src/customtabs/src/android/support/customtabs/CustomTabsIntent.java", "src/customtabs/src/android/support/customtabs/CustomTabsIntent.java",
......
...@@ -13,4 +13,7 @@ customization, callback setup, pre-warming and pre-fetching, and lifecycle ...@@ -13,4 +13,7 @@ customization, callback setup, pre-warming and pre-fetching, and lifecycle
management. Also inside demos there is another application that launches management. Also inside demos there is another application that launches
custom tabs in different modes. custom tabs in different modes.
The example applicaton also presents how to use Browser Actions, including
creating request intent and adding custom items.
Local Modifications: none Local Modifications: none
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