Skip to content

Commit

Permalink
[Android] Add a transparent Activity in Chrome to handle Browser Acti…
Browse files Browse the repository at this point in the history
…on 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}
  • Loading branch information
LuciferTian2010 authored and Commit bot committed Apr 28, 2017
1 parent ca74aee commit 3e1e45d
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 1 deletion.
2 changes: 1 addition & 1 deletion DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ deps_os = {
Var('chromium_git') + '/external/github.com/kennethreitz/requests.git' + '@' + 'f172b30356d821d180fa4ecfa3e71c7274a32de4',

'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':
Var('chromium_git') + '/external/github.com/googlevr/gvr-android-sdk.git' + '@' + '8d1395957283ee13ebe2bc672ba24e5ca4ec343f',
Expand Down
1 change: 1 addition & 0 deletions chrome/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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_recyclerview_java",
"//third_party/cacheinvalidation:cacheinvalidation_javalib",
"//third_party/custom_tabs_client:custom_tabs_support_java",
"//third_party/hamcrest:hamcrest_java",
"//ui/android:ui_java",
"//url/mojo:url_mojom_gurl_java",
Expand Down
15 changes: 15 additions & 0 deletions chrome/android/java/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,21 @@ by a child template that "extends" this file.
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize" >
</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
android:name="org.chromium.chrome.browser.physicalweb.PhysicalWebBroadcastService"
Expand Down
9 changes: 9 additions & 0 deletions chrome/android/java/res/values-v17/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@
<item name="windowNoTitle">true</item>
</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">
<item name="windowActionBar">false</item>
</style>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// 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();
}
}
2 changes: 2 additions & 0 deletions chrome/android/java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ chrome_java_sources = [
"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/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/childaccounts/ChildAccountFeedbackReporter.java",
"java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java",
Expand Down Expand Up @@ -1635,6 +1636,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/DisableHistogramsRule.java",
"junit/src/org/chromium/chrome/browser/ShortcutHelperTest.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/CompositorSurfaceManagerTest.java",
"junit/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchInternalStateTest.java",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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);
}
}
2 changes: 2 additions & 0 deletions third_party/custom_tabs_client/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ android_library("custom_tabs_client_shared_java") {

android_library("custom_tabs_support_java") {
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/CustomTabsClient.java",
"src/customtabs/src/android/support/customtabs/CustomTabsIntent.java",
Expand Down
3 changes: 3 additions & 0 deletions third_party/custom_tabs_client/README.chromium
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ customization, callback setup, pre-warming and pre-fetching, and lifecycle
management. Also inside demos there is another application that launches
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

0 comments on commit 3e1e45d

Please sign in to comment.