Commit e01199c9 authored by Michael Thiessen's avatar Michael Thiessen Committed by Commit Bot

Add new shortcut type for Touchless mode - launching shortcuts as WebApps

We want shortcuts to feel more app-like as there are no tabs in
touchless mode and shortcuts clobbering your tab is confusing amongst
other things.

See bug for additional details.

Bug: 977198
Change-Id: I47c58ce218ffe6d0a146228fcd28c6c2e9b3abf0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1669235
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarPeter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672094}
parent 2b17163b
...@@ -723,6 +723,16 @@ by a child template that "extends" this file. ...@@ -723,6 +723,16 @@ by a child template that "extends" this file.
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Non-exported alias that only Chrome can launch to verify shortcut intent comes from
Chrome (ShortcutManager on O+ launches under the shortcut creator's uid). -->
<activity-alias android:name="org.chromium.chrome.browser.webapps.SecureWebAppLauncher"
android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity"
android:exported="false">
<intent-filter>
<action android:name="org.chromium.chrome.browser.webapps.WebappManager.ACTION_START_SECURE_WEBAPP" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
<activity-alias android:name="com.google.android.apps.chrome.webapps.WebappManager" <activity-alias android:name="com.google.android.apps.chrome.webapps.WebappManager"
android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity"> android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity">
</activity-alias> </activity-alias>
......
...@@ -955,6 +955,16 @@ ...@@ -955,6 +955,16 @@
<data android:scheme="file"/> <data android:scheme="file"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity-alias
android:exported="false"
android:name="org.chromium.chrome.browser.webapps.SecureWebAppLauncher"
android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity">
<intent-filter>
<action
android:name="org.chromium.chrome.browser.webapps.WebappManager.ACTION_START_SECURE_WEBAPP"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
<activity-alias <activity-alias
android:exported="true" android:exported="true"
android:name="com.google.android.apps.chrome.IntentDispatcher" android:name="com.google.android.apps.chrome.IntentDispatcher"
......
...@@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable; ...@@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
...@@ -39,6 +40,8 @@ import org.chromium.base.annotations.CalledByNative; ...@@ -39,6 +40,8 @@ import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.blink_public.platform.WebDisplayMode; import org.chromium.blink_public.platform.WebDisplayMode;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabThemeColorHelper;
import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.webapps.WebApkInfo; import org.chromium.chrome.browser.webapps.WebApkInfo;
import org.chromium.chrome.browser.webapps.WebappActivity; import org.chromium.chrome.browser.webapps.WebappActivity;
...@@ -48,6 +51,7 @@ import org.chromium.chrome.browser.webapps.WebappLauncherActivity; ...@@ -48,6 +51,7 @@ import org.chromium.chrome.browser.webapps.WebappLauncherActivity;
import org.chromium.chrome.browser.webapps.WebappRegistry; import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.chrome.browser.widget.RoundedIconGenerator; import org.chromium.chrome.browser.widget.RoundedIconGenerator;
import org.chromium.content_public.common.ScreenOrientationConstants; import org.chromium.content_public.common.ScreenOrientationConstants;
import org.chromium.content_public.common.ScreenOrientationValues;
import org.chromium.ui.widget.Toast; import org.chromium.ui.widget.Toast;
import org.chromium.webapk.lib.client.WebApkValidator; import org.chromium.webapk.lib.client.WebApkValidator;
...@@ -189,20 +193,30 @@ public class ShortcutHelper { ...@@ -189,20 +193,30 @@ public class ShortcutHelper {
final String userTitle, final String name, final String shortName, final String iconUrl, final String userTitle, final String name, final String shortName, final String iconUrl,
final Bitmap icon, boolean isIconAdaptive, @WebDisplayMode final int displayMode, final Bitmap icon, boolean isIconAdaptive, @WebDisplayMode final int displayMode,
final int orientation, final int source, final long themeColor, final int orientation, final int source, final long themeColor,
final long backgroundColor, final long callbackPointer) { final long backgroundColor, final long callbackPointer,
final boolean isShortcutAsWebapp) {
new AsyncTask<Intent>() { new AsyncTask<Intent>() {
@Override @Override
protected Intent doInBackground() { protected Intent doInBackground() {
// Encoding {@link icon} as a string and computing the mac are expensive. // Encoding {@link icon} as a string and computing the mac are expensive.
// Shortcuts as Webapps on O+ launch into a non-exported component for verification.
boolean usesMacForVerification =
!isShortcutAsWebapp || Build.VERSION.SDK_INT < Build.VERSION_CODES.O;
// Encode the icon as a base64 string (Launcher drops Bitmaps in the Intent). // Encode the icon as a base64 string (Launcher drops Bitmaps in the Intent).
String encodedIcon = encodeBitmapAsString(icon); String encodedIcon = encodeBitmapAsString(icon);
Intent shortcutIntent = createWebappShortcutIntent(id, String action = usesMacForVerification
sDelegate.getFullscreenAction(), url, scopeUrl, name, shortName, ? sDelegate.getFullscreenAction()
encodedIcon, WEBAPP_SHORTCUT_VERSION, displayMode, orientation, themeColor, : WebappLauncherActivity.ACTION_START_SECURE_WEBAPP;
backgroundColor, iconUrl.isEmpty(), isIconAdaptive); Intent shortcutIntent = createWebappShortcutIntent(id, action, url, scopeUrl, name,
shortcutIntent.putExtra(EXTRA_MAC, getEncodedMac(url)); shortName, encodedIcon, WEBAPP_SHORTCUT_VERSION, displayMode, orientation,
themeColor, backgroundColor, iconUrl.isEmpty(), isIconAdaptive);
if (usesMacForVerification) {
shortcutIntent.putExtra(EXTRA_MAC, getEncodedMac(url));
}
shortcutIntent.putExtra(EXTRA_SOURCE, source); shortcutIntent.putExtra(EXTRA_SOURCE, source);
return shortcutIntent; return shortcutIntent;
} }
...@@ -213,11 +227,10 @@ public class ShortcutHelper { ...@@ -213,11 +227,10 @@ public class ShortcutHelper {
// Store the webapp data so that it is accessible without the intent. Once this // Store the webapp data so that it is accessible without the intent. Once this
// process is complete, call back to native code to start the splash image // process is complete, call back to native code to start the splash image
// download. // download.
WebappRegistry.getInstance().register( WebappRegistry.getInstance().register(id, storage -> {
id, storage -> { storage.updateFromShortcutIntent(resultIntent);
storage.updateFromShortcutIntent(resultIntent); if (callbackPointer != 0) nativeOnWebappDataStored(callbackPointer);
nativeOnWebappDataStored(callbackPointer); });
});
if (shouldShowToastWhenAddingShortcut()) { if (shouldShowToastWhenAddingShortcut()) {
showAddedToHomescreenToast(userTitle); showAddedToHomescreenToast(userTitle);
} }
...@@ -231,13 +244,23 @@ public class ShortcutHelper { ...@@ -231,13 +244,23 @@ public class ShortcutHelper {
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@CalledByNative @CalledByNative
public static void addShortcut(String id, String url, String userTitle, Bitmap icon, public static void addShortcut(@Nullable Tab tab, String id, String url, String userTitle,
boolean isIconAdaptive, int source) { Bitmap icon, boolean isIconAdaptive, int source, String iconUrl) {
Context context = ContextUtils.getApplicationContext(); if (FeatureUtilities.isNoTouchModeEnabled()) {
final Intent shortcutIntent = createShortcutIntent(url); // There are no tabs in NoTouchMode, so we want to give shortcuts a more app-like
// experience.
long themeColor = (tab == null) ? MANIFEST_COLOR_INVALID_OR_MISSING
: TabThemeColorHelper.getColor(tab);
addWebapp(id, url, getScopeFromUrl(url), userTitle, userTitle, userTitle, iconUrl, icon,
isIconAdaptive, WebDisplayMode.STANDALONE, ScreenOrientationValues.DEFAULT,
source, themeColor, MANIFEST_COLOR_INVALID_OR_MISSING, 0 /* callbackPointer */,
true /* isShortcutAsWebapp */);
return;
}
Intent shortcutIntent = createShortcutIntent(url);
shortcutIntent.putExtra(EXTRA_ID, id); shortcutIntent.putExtra(EXTRA_ID, id);
shortcutIntent.putExtra(EXTRA_SOURCE, source); shortcutIntent.putExtra(EXTRA_SOURCE, source);
shortcutIntent.setPackage(context.getPackageName()); shortcutIntent.setPackage(ContextUtils.getApplicationContext().getPackageName());
sDelegate.addShortcutToHomescreen(userTitle, icon, isIconAdaptive, shortcutIntent); sDelegate.addShortcutToHomescreen(userTitle, icon, isIconAdaptive, shortcutIntent);
if (shouldShowToastWhenAddingShortcut()) { if (shouldShowToastWhenAddingShortcut()) {
showAddedToHomescreenToast(userTitle); showAddedToHomescreenToast(userTitle);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.webapps; package org.chromium.chrome.browser.webapps;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
...@@ -48,6 +49,11 @@ public class WebappLauncherActivity extends Activity { ...@@ -48,6 +49,11 @@ public class WebappLauncherActivity extends Activity {
public static final String ACTION_START_WEBAPP = public static final String ACTION_START_WEBAPP =
"com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP"; "com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP";
public static final String SECURE_WEBAPP_LAUNCHER =
"org.chromium.chrome.browser.webapps.SecureWebAppLauncher";
public static final String ACTION_START_SECURE_WEBAPP =
"org.chromium.chrome.browser.webapps.WebappManager.ACTION_START_SECURE_WEBAPP";
/** /**
* Delay in ms for relaunching WebAPK as a result of getting intent with extra * Delay in ms for relaunching WebAPK as a result of getting intent with extra
* {@link WebApkConstants.EXTRA_RELAUNCH}. The delay was chosen arbirtarily and seems to * {@link WebApkConstants.EXTRA_RELAUNCH}. The delay was chosen arbirtarily and seems to
...@@ -157,6 +163,12 @@ public class WebappLauncherActivity extends Activity { ...@@ -157,6 +163,12 @@ public class WebappLauncherActivity extends Activity {
// does not specify required values such as the uri. // does not specify required values such as the uri.
if (webappInfo == null) return false; if (webappInfo == null) return false;
// The component is not exported and can only be launched by Chrome.
if (intent.getComponent().equals(new ComponentName(
ContextUtils.getApplicationContext(), SECURE_WEBAPP_LAUNCHER))) {
return true;
}
String webappUrl = webappInfo.uri().toString(); String webappUrl = webappInfo.uri().toString();
String webappMac = IntentUtils.safeGetStringExtra(intent, ShortcutHelper.EXTRA_MAC); String webappMac = IntentUtils.safeGetStringExtra(intent, ShortcutHelper.EXTRA_MAC);
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
package org.chromium.chrome.browser.touchless; package org.chromium.chrome.browser.touchless;
import android.app.Activity;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.webapps.AddToHomescreenDialog; import org.chromium.chrome.browser.webapps.AddToHomescreenDialog;
import org.chromium.chrome.browser.webapps.TouchlessAddToHomescreenDialog; import org.chromium.chrome.browser.webapps.TouchlessAddToHomescreenDialog;
...@@ -16,14 +16,14 @@ import org.chromium.ui.modaldialog.ModalDialogManager; ...@@ -16,14 +16,14 @@ import org.chromium.ui.modaldialog.ModalDialogManager;
* Add to homescreen manager specifically for touchless devices. * Add to homescreen manager specifically for touchless devices.
*/ */
class TouchlessAddToHomescreenManager implements AddToHomescreenDialog.Delegate { class TouchlessAddToHomescreenManager implements AddToHomescreenDialog.Delegate {
private final Activity mActivity; private final ChromeActivity mActivity;
private final ModalDialogManager mDialogManager; private final ModalDialogManager mDialogManager;
private final String mUrl; private final String mUrl;
private final String mTitle; private final String mTitle;
private final Bitmap mIconBitmap; private final Bitmap mIconBitmap;
public TouchlessAddToHomescreenManager(Activity activity, ModalDialogManager dialogManager, public TouchlessAddToHomescreenManager(ChromeActivity activity,
String url, String title, Bitmap iconBitmap) { ModalDialogManager dialogManager, String url, String title, Bitmap iconBitmap) {
mActivity = activity; mActivity = activity;
mDialogManager = dialogManager; mDialogManager = dialogManager;
mUrl = url; mUrl = url;
...@@ -42,7 +42,8 @@ class TouchlessAddToHomescreenManager implements AddToHomescreenDialog.Delegate ...@@ -42,7 +42,8 @@ class TouchlessAddToHomescreenManager implements AddToHomescreenDialog.Delegate
@Override @Override
public void addToHomescreen(String title) { public void addToHomescreen(String title) {
ShortcutHelper.addShortcut(mUrl, mUrl, title, mIconBitmap, false, 0); ShortcutHelper.addShortcut(
mActivity.getActivityTab(), mUrl, mUrl, title, mIconBitmap, false, 0, "");
} }
@Override @Override
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
package org.chromium.chrome.browser.touchless; package org.chromium.chrome.browser.touchless;
import android.app.Activity;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.support.annotation.DrawableRes; import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.native_page.ContextMenuManager; import org.chromium.chrome.browser.native_page.ContextMenuManager;
import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate; import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties; import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties;
...@@ -59,12 +59,12 @@ public class TouchlessContextMenuManager extends ContextMenuManager { ...@@ -59,12 +59,12 @@ public class TouchlessContextMenuManager extends ContextMenuManager {
} }
} }
private final Activity mActivity; private final ChromeActivity mActivity;
private final ModalDialogManager mDialogManager; private final ModalDialogManager mDialogManager;
private PropertyModel mTouchlessMenuModel; private PropertyModel mTouchlessMenuModel;
private ModalDialogManager mModalDialogManager; private ModalDialogManager mModalDialogManager;
public TouchlessContextMenuManager(Activity activity, ModalDialogManager dialogManager, public TouchlessContextMenuManager(ChromeActivity activity, ModalDialogManager dialogManager,
NativePageNavigationDelegate navigationDelegate, NativePageNavigationDelegate navigationDelegate,
TouchEnabledDelegate touchEnabledDelegate, Runnable closeContextMenuCallback, TouchEnabledDelegate touchEnabledDelegate, Runnable closeContextMenuCallback,
String userActionPrefix) { String userActionPrefix) {
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/android/chrome_jni_headers/ShortcutHelper_jni.h" #include "chrome/android/chrome_jni_headers/ShortcutHelper_jni.h"
#include "chrome/browser/android/color_helpers.h" #include "chrome/browser/android/color_helpers.h"
#include "chrome/browser/android/tab_android.h"
#include "chrome/browser/android/webapk/chrome_webapk_host.h" #include "chrome/browser/android/webapk/chrome_webapk_host.h"
#include "chrome/browser/android/webapk/webapk_install_service.h" #include "chrome/browser/android/webapk/webapk_install_service.h"
#include "chrome/browser/android/webapk/webapk_metrics.h" #include "chrome/browser/android/webapk/webapk_metrics.h"
...@@ -114,11 +115,13 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info, ...@@ -114,11 +115,13 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info,
java_short_name, java_best_primary_icon_url, java_bitmap, java_short_name, java_best_primary_icon_url, java_bitmap,
is_icon_maskable, info.display, info.orientation, info.source, is_icon_maskable, info.display, info.orientation, info.source,
OptionalSkColorToJavaColor(info.theme_color), OptionalSkColorToJavaColor(info.theme_color),
OptionalSkColorToJavaColor(info.background_color), callback_pointer); OptionalSkColorToJavaColor(info.background_color), callback_pointer,
false /* isShortcutAsWebapp */);
} }
// Adds a shortcut which opens in a browser tab to the launcher. // Adds a shortcut which opens in a browser tab to the launcher.
void AddShortcutWithSkBitmap(const ShortcutInfo& info, void AddShortcutWithSkBitmap(content::WebContents* web_contents,
const ShortcutInfo& info,
const std::string& id, const std::string& id,
const SkBitmap& icon_bitmap, const SkBitmap& icon_bitmap,
bool is_icon_maskable) { bool is_icon_maskable) {
...@@ -129,12 +132,17 @@ void AddShortcutWithSkBitmap(const ShortcutInfo& info, ...@@ -129,12 +132,17 @@ void AddShortcutWithSkBitmap(const ShortcutInfo& info,
base::android::ConvertUTF8ToJavaString(env, info.url.spec()); base::android::ConvertUTF8ToJavaString(env, info.url.spec());
ScopedJavaLocalRef<jstring> java_user_title = ScopedJavaLocalRef<jstring> java_user_title =
base::android::ConvertUTF16ToJavaString(env, info.user_title); base::android::ConvertUTF16ToJavaString(env, info.user_title);
ScopedJavaLocalRef<jstring> java_best_primary_icon_url =
base::android::ConvertUTF8ToJavaString(env,
info.best_primary_icon_url.spec());
ScopedJavaLocalRef<jobject> java_bitmap; ScopedJavaLocalRef<jobject> java_bitmap;
if (!icon_bitmap.drawsNothing()) if (!icon_bitmap.drawsNothing())
java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap); java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap);
TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
Java_ShortcutHelper_addShortcut(env, java_id, java_url, java_user_title, Java_ShortcutHelper_addShortcut(env, tab ? tab->GetJavaObject() : nullptr,
java_bitmap, is_icon_maskable, info.source); java_id, java_url, java_user_title,
java_bitmap, is_icon_maskable, info.source,
java_best_primary_icon_url);
} }
} // anonymous namespace } // anonymous namespace
...@@ -183,7 +191,8 @@ void ShortcutHelper::AddToLauncherWithSkBitmap( ...@@ -183,7 +191,8 @@ void ShortcutHelper::AddToLauncherWithSkBitmap(
info.minimum_splash_image_size_in_px, webapp_id)); info.minimum_splash_image_size_in_px, webapp_id));
return; return;
} }
AddShortcutWithSkBitmap(info, webapp_id, icon_bitmap, is_icon_maskable); AddShortcutWithSkBitmap(web_contents, info, webapp_id, icon_bitmap,
is_icon_maskable);
} }
void ShortcutHelper::ShowWebApkInstallInProgressToast() { void ShortcutHelper::ShowWebApkInstallInProgressToast() {
......
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