Commit 8d9ec21b authored by Pavol Drotar's avatar Pavol Drotar Committed by Commit Bot

Add site settings to WebApks

The state of WebApks is split across android settings and chrome
site settings. This change adds a dynamic android shortcut into
WebApks to make site settings more accessible.

The ManageDataLauncherActivity adds functionality for adding the
shortcut, checking whether the version of Chrome supports site settings,
and launching site settings through the chrome
ManageTrustedWebActivityLauncherActivity.
ManageTrustedWebActivityLauncherActivity was modified to include support
for WebApk verification.

Bug: 1119727
Change-Id: I90720ac83c4edf90056cc7b2692be1079b8643a4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2387764Reviewed-by: default avatarPeter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Commit-Queue: Pavol Drotár <pavoldrotar@google.com>
Auto-Submit: Pavol Drotár <pavoldrotar@google.com>
Cr-Commit-Position: refs/heads/master@{#809840}
parent f1208e12
...@@ -73,6 +73,7 @@ chrome_test_java_sources = [ ...@@ -73,6 +73,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoDismissTest.java",
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java",
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java",
"javatests/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivityTest.java",
"javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java", "javatests/src/org/chromium/chrome/browser/browserservices/OriginVerifierTest.java",
"javatests/src/org/chromium/chrome/browser/browserservices/QualityEnforcerTest.java", "javatests/src/org/chromium/chrome/browser/browserservices/QualityEnforcerTest.java",
"javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java", "javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java",
......
...@@ -14,6 +14,7 @@ import org.chromium.base.Log; ...@@ -14,6 +14,7 @@ import org.chromium.base.Log;
import org.chromium.chrome.browser.ChromeApplication; import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer; import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.webapk.lib.common.WebApkConstants;
/** /**
* Launched by Trusted Web Activity apps when the user clears data. * Launched by Trusted Web Activity apps when the user clears data.
...@@ -28,11 +29,14 @@ public class ManageTrustedWebActivityDataActivity extends AppCompatActivity { ...@@ -28,11 +29,14 @@ public class ManageTrustedWebActivityDataActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
launchSettings();
String urlToLaunchSettingsFor = getIntent().getData().toString();
boolean isWebApk = getIntent().getBooleanExtra(WebApkConstants.EXTRA_IS_WEBAPK, false);
launchSettings(urlToLaunchSettingsFor, isWebApk);
finish(); finish();
} }
private void launchSettings() { private void launchSettings(@Nullable String urlToLaunchSettingsFor, boolean isWebApk) {
String packageName = getClientPackageName(); String packageName = getClientPackageName();
if (packageName == null) { if (packageName == null) {
logNoPackageName(); logNoPackageName();
...@@ -41,8 +45,14 @@ public class ManageTrustedWebActivityDataActivity extends AppCompatActivity { ...@@ -41,8 +45,14 @@ public class ManageTrustedWebActivityDataActivity extends AppCompatActivity {
} }
new TrustedWebActivityUmaRecorder(ChromeBrowserInitializer.getInstance()) new TrustedWebActivityUmaRecorder(ChromeBrowserInitializer.getInstance())
.recordOpenedSettingsViaManageSpace(); .recordOpenedSettingsViaManageSpace();
if (isWebApk) {
TrustedWebActivitySettingsLauncher.launchForWebApkPackageName(
this, packageName, urlToLaunchSettingsFor);
} else {
TrustedWebActivitySettingsLauncher.launchForPackageName(this, packageName); TrustedWebActivitySettingsLauncher.launchForPackageName(this, packageName);
} }
}
@Nullable @Nullable
private String getClientPackageName() { private String getClientPackageName() {
......
...@@ -13,10 +13,12 @@ import org.chromium.base.Log; ...@@ -13,10 +13,12 @@ import org.chromium.base.Log;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.settings.SettingsLauncher; import org.chromium.chrome.browser.settings.SettingsLauncher;
import org.chromium.chrome.browser.settings.SettingsLauncherImpl; import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
import org.chromium.components.browser_ui.site_settings.AllSiteSettings; import org.chromium.components.browser_ui.site_settings.AllSiteSettings;
import org.chromium.components.browser_ui.site_settings.SettingsNavigationSource; import org.chromium.components.browser_ui.site_settings.SettingsNavigationSource;
import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings; import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory;
import org.chromium.components.webapk.lib.client.WebApkValidator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
...@@ -33,13 +35,8 @@ public class TrustedWebActivitySettingsLauncher { ...@@ -33,13 +35,8 @@ public class TrustedWebActivitySettingsLauncher {
* able to work with each of them. * able to work with each of them.
*/ */
public static void launchForPackageName(Context context, String packageName) { public static void launchForPackageName(Context context, String packageName) {
int applicationUid; Integer applicationUid = getApplicationUid(context, packageName);
try { if (applicationUid == null) return;
applicationUid = context.getPackageManager().getApplicationInfo(packageName, 0).uid;
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Package " + packageName + " not found");
return;
}
ClientAppDataRegister register = new ClientAppDataRegister(); ClientAppDataRegister register = new ClientAppDataRegister();
Collection<String> domains = register.getDomainsForRegisteredUid(applicationUid); Collection<String> domains = register.getDomainsForRegisteredUid(applicationUid);
...@@ -51,6 +48,33 @@ public class TrustedWebActivitySettingsLauncher { ...@@ -51,6 +48,33 @@ public class TrustedWebActivitySettingsLauncher {
launch(context, origins, domains); launch(context, origins, domains);
} }
/**
* Launches site-settings for a WebApk with a given package name and associated url.
*/
public static void launchForWebApkPackageName(
Context context, String packageName, String webApkUrl) {
if (!WebApkValidator.canWebApkHandleUrl(context, packageName, webApkUrl)) {
Log.d(TAG, "WebApk " + packageName + " can't handle url " + webApkUrl);
return;
}
if (getApplicationUid(context, packageName) == null) return;
// Handle the case when settings are selected but Chrome was not running.
ChromeWebApkHost.init();
openSingleWebsitePrefs(context, webApkUrl);
}
private static Integer getApplicationUid(Context context, String packageName) {
int applicationUid;
try {
applicationUid = context.getPackageManager().getApplicationInfo(packageName, 0).uid;
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Package " + packageName + " not found");
return null;
}
return applicationUid;
}
/** /**
* Same as above, but with list of associated origins and domains already retrieved. * Same as above, but with list of associated origins and domains already retrieved.
*/ */
......
// Copyright 2020 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.browserservices;
import static junit.framework.Assert.assertTrue;
import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.createSession;
import static org.chromium.chrome.browser.browserservices.TrustedWebActivityTestUtil.spoofVerification;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.test.InstrumentationRegistry;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsSession;
import androidx.test.filters.MediumTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.webapk.lib.client.WebApkValidator;
import org.chromium.webapk.lib.common.WebApkConstants;
import java.util.concurrent.TimeoutException;
/**
* Instrumentation tests for launching site settings for WebApks.
* Site settings are added as a dynamic android shortcut.
* The shortcut launches a {@link ManageTrustedWebActivityDataActivity}
* intent that validates the WebApk and launches the chromium SettingsActivity.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ManageTrustedWebActivityDataActivityTest {
private static final String SETTINGS_ACTIVITY_NAME =
"org.chromium.chrome.browser.settings.SettingsActivity";
private static final String WEBAPK_TEST_URL = "https://padr31.github.io";
private static final String TEST_PACKAGE_NAME =
InstrumentationRegistry.getTargetContext().getPackageName();
@Test
@MediumTest
public void launchesWebApkSiteSettings() {
Intent siteSettingsIntent =
createWebApkSiteSettingsIntent(TEST_PACKAGE_NAME, Uri.parse(WEBAPK_TEST_URL));
WebApkValidator.setDisableValidationForTesting(true);
try {
launchSiteSettingsIntent(siteSettingsIntent);
// Check settings activity is running.
boolean settingsActivityRunning = false;
for (Activity a : ApplicationStatus.getRunningActivities()) {
String activityName =
a.getPackageManager().getActivityInfo(a.getComponentName(), 0).name;
if (activityName.equals(SETTINGS_ACTIVITY_NAME)) {
settingsActivityRunning = true;
}
}
assertTrue(settingsActivityRunning);
} catch (TimeoutException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
private static Intent createWebApkSiteSettingsIntent(String packageName, Uri uri) {
// CustomTabsIntent builder is used just to put in the session extras.
CustomTabsIntent.Builder builder =
new CustomTabsIntent.Builder(CustomTabsSession.createMockSessionForTesting(
new ComponentName(InstrumentationRegistry.getTargetContext(),
ManageTrustedWebActivityDataActivity.class)));
Intent intent = builder.build().intent;
intent.setAction(
"android.support.customtabs.action.ACTION_MANAGE_TRUSTED_WEB_ACTIVITY_DATA");
intent.setPackage(packageName);
intent.setData(uri);
intent.putExtra(WebApkConstants.EXTRA_IS_WEBAPK, true);
// The following flag is required because the test starts the intent outside of an activity.
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
public void launchSiteSettingsIntent(Intent intent) throws TimeoutException {
String url = intent.getData().toString();
spoofVerification(TEST_PACKAGE_NAME, url);
createSession(intent, TEST_PACKAGE_NAME);
InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
}
}
...@@ -33,6 +33,7 @@ public final class WebApkConstants { ...@@ -33,6 +33,7 @@ public final class WebApkConstants {
"org.chromium.chrome.browser.webapk.splash_provided_by_webapk"; "org.chromium.chrome.browser.webapk.splash_provided_by_webapk";
// Tells the host browser to relaunch the WebAPK. // Tells the host browser to relaunch the WebAPK.
public static final String EXTRA_RELAUNCH = "org.chromium.webapk.relaunch"; public static final String EXTRA_RELAUNCH = "org.chromium.webapk.relaunch";
public static final String EXTRA_IS_WEBAPK = "org.chromium.webapk.is_webapk";
// Must be kept in sync with chrome/browser/android/shortcut_info.h. // Must be kept in sync with chrome/browser/android/shortcut_info.h.
public static final int SHORTCUT_SOURCE_UNKNOWN = 0; public static final int SHORTCUT_SOURCE_UNKNOWN = 0;
......
...@@ -59,6 +59,10 @@ ...@@ -59,6 +59,10 @@
{{{raw_intent_filters}}} {{{raw_intent_filters}}}
</activity> </activity>
<activity android:name="org.chromium.webapk.shell_apk.ManageDataLauncherActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity>
<activity android:name="org.chromium.webapk.shell_apk.h2o.H2OOpaqueMainActivity" <activity android:name="org.chromium.webapk.shell_apk.h2o.H2OOpaqueMainActivity"
android:theme="@style/SplashTheme" android:theme="@style/SplashTheme"
android:relinquishTaskIdentity="true" android:relinquishTaskIdentity="true"
......
...@@ -60,6 +60,7 @@ template("webapk_java") { ...@@ -60,6 +60,7 @@ template("webapk_java") {
"src/org/chromium/webapk/shell_apk/IdentityService.java", "src/org/chromium/webapk/shell_apk/IdentityService.java",
"src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java", "src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java",
"src/org/chromium/webapk/shell_apk/LaunchHostBrowserSelector.java", "src/org/chromium/webapk/shell_apk/LaunchHostBrowserSelector.java",
"src/org/chromium/webapk/shell_apk/ManageDataLauncherActivity.java",
"src/org/chromium/webapk/shell_apk/TransparentLauncherActivity.java", "src/org/chromium/webapk/shell_apk/TransparentLauncherActivity.java",
"src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java", "src/org/chromium/webapk/shell_apk/WebApkSharedPreferences.java",
"src/org/chromium/webapk/shell_apk/WebApkUtils.java", "src/org/chromium/webapk/shell_apk/WebApkUtils.java",
...@@ -76,6 +77,7 @@ template("webapk_java") { ...@@ -76,6 +77,7 @@ template("webapk_java") {
"//chrome/android/webapk/libs/common:splash_java", "//chrome/android/webapk/libs/common:splash_java",
"//components/webapk/android/libs/common:java", "//components/webapk/android/libs/common:java",
"//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_sdk/androidx_browser:androidx_browser_java",
] ]
} }
} }
...@@ -151,6 +153,7 @@ template("webapk_tmpl") { ...@@ -151,6 +153,7 @@ template("webapk_tmpl") {
android_resources(_resources_target_name) { android_resources(_resources_target_name) {
create_srcjar = false create_srcjar = false
sources = [ sources = [
"res/drawable-hdpi/ic_site_settings.png",
"res/drawable-hdpi/last_resort_runtime_host_logo.png", "res/drawable-hdpi/last_resort_runtime_host_logo.png",
"res/drawable-hdpi/notification_badge.png", "res/drawable-hdpi/notification_badge.png",
"res/drawable-hdpi/shortcut_1_icon.png", "res/drawable-hdpi/shortcut_1_icon.png",
...@@ -158,24 +161,28 @@ template("webapk_tmpl") { ...@@ -158,24 +161,28 @@ template("webapk_tmpl") {
"res/drawable-hdpi/shortcut_3_icon.png", "res/drawable-hdpi/shortcut_3_icon.png",
"res/drawable-hdpi/shortcut_4_icon.png", "res/drawable-hdpi/shortcut_4_icon.png",
"res/drawable-hdpi/splash_icon.xml", "res/drawable-hdpi/splash_icon.xml",
"res/drawable-mdpi/ic_site_settings.png",
"res/drawable-mdpi/notification_badge.png", "res/drawable-mdpi/notification_badge.png",
"res/drawable-mdpi/shortcut_1_icon.png", "res/drawable-mdpi/shortcut_1_icon.png",
"res/drawable-mdpi/shortcut_2_icon.png", "res/drawable-mdpi/shortcut_2_icon.png",
"res/drawable-mdpi/shortcut_3_icon.png", "res/drawable-mdpi/shortcut_3_icon.png",
"res/drawable-mdpi/shortcut_4_icon.png", "res/drawable-mdpi/shortcut_4_icon.png",
"res/drawable-mdpi/splash_icon.xml", "res/drawable-mdpi/splash_icon.xml",
"res/drawable-xhdpi/ic_site_settings.png",
"res/drawable-xhdpi/notification_badge.png", "res/drawable-xhdpi/notification_badge.png",
"res/drawable-xhdpi/shortcut_1_icon.png", "res/drawable-xhdpi/shortcut_1_icon.png",
"res/drawable-xhdpi/shortcut_2_icon.png", "res/drawable-xhdpi/shortcut_2_icon.png",
"res/drawable-xhdpi/shortcut_3_icon.png", "res/drawable-xhdpi/shortcut_3_icon.png",
"res/drawable-xhdpi/shortcut_4_icon.png", "res/drawable-xhdpi/shortcut_4_icon.png",
"res/drawable-xhdpi/splash_icon.xml", "res/drawable-xhdpi/splash_icon.xml",
"res/drawable-xxhdpi/ic_site_settings.png",
"res/drawable-xxhdpi/notification_badge.png", "res/drawable-xxhdpi/notification_badge.png",
"res/drawable-xxhdpi/shortcut_1_icon.png", "res/drawable-xxhdpi/shortcut_1_icon.png",
"res/drawable-xxhdpi/shortcut_2_icon.png", "res/drawable-xxhdpi/shortcut_2_icon.png",
"res/drawable-xxhdpi/shortcut_3_icon.png", "res/drawable-xxhdpi/shortcut_3_icon.png",
"res/drawable-xxhdpi/shortcut_4_icon.png", "res/drawable-xxhdpi/shortcut_4_icon.png",
"res/drawable-xxhdpi/splash_icon.xml", "res/drawable-xxhdpi/splash_icon.xml",
"res/drawable-xxxhdpi/ic_site_settings.png",
"res/drawable-xxxhdpi/notification_badge.png", "res/drawable-xxxhdpi/notification_badge.png",
"res/drawable-xxxhdpi/shortcut_1_icon.png", "res/drawable-xxxhdpi/shortcut_1_icon.png",
"res/drawable-xxxhdpi/shortcut_2_icon.png", "res/drawable-xxxhdpi/shortcut_2_icon.png",
......
...@@ -34,8 +34,6 @@ public class HostBrowserLauncher { ...@@ -34,8 +34,6 @@ public class HostBrowserLauncher {
* Otherwise, launches the host browser in tabbed mode. * Otherwise, launches the host browser in tabbed mode.
*/ */
public static void launch(Activity activity, HostBrowserLauncherParams params) { public static void launch(Activity activity, HostBrowserLauncherParams params) {
Log.v(TAG, "WebAPK Launch URL: " + params.getStartUrl());
if (HostBrowserUtils.shouldLaunchInTab(params)) { if (HostBrowserUtils.shouldLaunchInTab(params)) {
launchInTab(activity.getApplicationContext(), params); launchInTab(activity.getApplicationContext(), params);
return; return;
...@@ -48,6 +46,8 @@ public class HostBrowserLauncher { ...@@ -48,6 +46,8 @@ public class HostBrowserLauncher {
/** Launches host browser in WebAPK mode. */ /** Launches host browser in WebAPK mode. */
public static void launchBrowserInWebApkMode(Activity activity, public static void launchBrowserInWebApkMode(Activity activity,
HostBrowserLauncherParams params, Bundle extraExtras, int flags, boolean expectResult) { HostBrowserLauncherParams params, Bundle extraExtras, int flags, boolean expectResult) {
ManageDataLauncherActivity.updateSiteSettingsShortcut(
activity.getApplicationContext(), params);
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(ACTION_START_WEBAPK); intent.setAction(ACTION_START_WEBAPK);
intent.setPackage(params.getHostBrowserPackageName()); intent.setPackage(params.getHostBrowserPackageName());
...@@ -102,6 +102,7 @@ public class HostBrowserLauncher { ...@@ -102,6 +102,7 @@ public class HostBrowserLauncher {
/** Launches a WebAPK in its runtime host browser as a tab. */ /** Launches a WebAPK in its runtime host browser as a tab. */
private static void launchInTab(Context context, HostBrowserLauncherParams params) { private static void launchInTab(Context context, HostBrowserLauncherParams params) {
ManageDataLauncherActivity.updateSiteSettingsShortcut(context, params);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(params.getStartUrl())); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(params.getStartUrl()));
intent.setPackage(params.getHostBrowserPackageName()); intent.setPackage(params.getHostBrowserPackageName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
......
// Copyright 2020 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.webapk.shell_apk;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import androidx.browser.customtabs.CustomTabsSession;
import org.chromium.webapk.lib.common.WebApkConstants;
import java.util.Collections;
/**
* A convenience class for adding site setting shortcuts into WebApks.
* The shortcut opens the web browser's site settings for the url
* associated to the WebApk.
*/
public class ManageDataLauncherActivity extends Activity {
private static final String TAG = "ManageDataLauncher";
public static final String ACTION_SITE_SETTINGS =
"android.support.customtabs.action.ACTION_MANAGE_TRUSTED_WEB_ACTIVITY_DATA";
public static final String SITE_SETTINGS_SHORTCUT_ID =
"android.support.customtabs.action.SITE_SETTINGS_SHORTCUT";
private static final String EXTRA_SITE_SETTINGS_URL = "SITE_SETTINGS_URL";
private static final String EXTRA_PROVIDER_PACKAGE = "PROVIDER_PACKAGE";
public static final int CHROMIUM_VERSION_SUPPORTS_WEBAPK_MANAGE_SPACE = 87;
@Nullable
private String mProviderPackage;
@Nullable
private CustomTabsServiceConnection mConnection;
@Nullable
private Uri mUrl;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mProviderPackage = getIntent().getStringExtra(EXTRA_PROVIDER_PACKAGE);
mUrl = Uri.parse(getIntent().getStringExtra(EXTRA_SITE_SETTINGS_URL));
if (!supportsManageSpace(this, mProviderPackage)) {
handleNoSupportForManageSpace();
return;
}
setContentView(createLoadingView());
mConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(
ComponentName componentName, CustomTabsClient client) {
if (!isFinishing()) {
launchSettings(client.newSession(null));
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {}
};
CustomTabsClient.bindCustomTabsService(this, mProviderPackage, mConnection);
}
/**
* Returns the url of the page for which the settings will be shown.
* The url must be provided as an intent extra to {@link ManageDataLauncherActivity}.
*/
@Nullable
private Uri getWebApkStartUrl() {
return mUrl;
}
/**
* Returns a view with a loading spinner.
*/
@NonNull
private View createLoadingView() {
ProgressBar progressBar = new ProgressBar(this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
params.gravity = Gravity.CENTER;
progressBar.setLayoutParams(params);
FrameLayout layout = new FrameLayout(this);
layout.addView(progressBar);
return layout;
}
/**
* Called if a TWA provider doesn't support manage space feature. The default behavior is to
* show a toast telling the user where the data is stored.
*/
private void handleNoSupportForManageSpace() {
String appName;
try {
ApplicationInfo info = getPackageManager().getApplicationInfo(mProviderPackage, 0);
appName = getPackageManager().getApplicationLabel(info).toString();
} catch (PackageManager.NameNotFoundException e) {
appName = mProviderPackage;
}
Toast.makeText(this, getString(R.string.no_support_for_manage_space, appName),
Toast.LENGTH_LONG)
.show();
finish();
}
@Override
protected void onStop() {
super.onStop();
if (mConnection != null) {
unbindService(mConnection);
}
finish();
}
private void launchSettings(CustomTabsSession session) {
boolean success =
launchBrowserSiteSettings(this, session, mProviderPackage, getWebApkStartUrl());
if (success) {
finish();
} else {
handleNoSupportForManageSpace();
}
}
private static boolean launchBrowserSiteSettings(
Activity activity, CustomTabsSession session, String packageName, Uri defaultUri) {
// A Custom Tabs Session is required so that the browser can verify this app's identity.
Intent intent = new CustomTabsIntent.Builder().setSession(session).build().intent;
intent.setAction(ACTION_SITE_SETTINGS);
intent.setPackage(packageName);
intent.setData(defaultUri);
intent.putExtra(WebApkConstants.EXTRA_IS_WEBAPK, true);
try {
activity.startActivity(intent);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
private static boolean supportsManageSpace(Context context, String providerPackage) {
return HostBrowserUtils.queryHostBrowserMajorChromiumVersion(context, providerPackage)
>= CHROMIUM_VERSION_SUPPORTS_WEBAPK_MANAGE_SPACE;
}
/**
* Returns the {@link ShortcutInfo} for a dynamic shortcut into site settings,
* provided that {@link ManageDataLauncherActivity} is present in the manifest
* and an Intent for managing site settings is available.
*
* Otherwise returns null if {@link ManageDataLauncherActivity} is not launchable
* or if shortcuts are not supported by the Android SDK version.
*
* The shortcut returned does not specify an activity. Thus when the shortcut is added,
* the app's main activity will be used by default. This activity needs to define the
* MAIN action and LAUNCHER category in order to attach the shortcut.
*/
@NonNull
@TargetApi(Build.VERSION_CODES.N_MR1)
private static ShortcutInfo createSiteSettingsShortcutInfo(
Context context, String url, String providerPackage) {
Intent siteSettingsIntent = new Intent(context, ManageDataLauncherActivity.class);
// Intent needs to have an action set, we can set an arbitrary action.
siteSettingsIntent.setAction(ACTION_SITE_SETTINGS);
siteSettingsIntent.putExtra(EXTRA_SITE_SETTINGS_URL, url);
siteSettingsIntent.putExtra(EXTRA_PROVIDER_PACKAGE, providerPackage);
return new ShortcutInfo.Builder(context, SITE_SETTINGS_SHORTCUT_ID)
.setShortLabel(context.getString(R.string.site_settings_short_label))
.setLongLabel(context.getString(R.string.site_settings_long_label))
.setIcon(Icon.createWithResource(context, R.drawable.ic_site_settings))
.setIntent(siteSettingsIntent)
.build();
}
/**
* Adds dynamic shortcut to site settings if the twa provider and android version supports it.
*
* Removes previously added site settings shortcut if it is no longer supported, e.g. the user
* changed their default browser.
*/
public static void updateSiteSettingsShortcut(
Context context, HostBrowserLauncherParams params) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return;
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
// Remove potentially existing shortcut if package does not support shortcuts.
if (!supportsManageSpace(context, params.getHostBrowserPackageName())) {
shortcutManager.removeDynamicShortcuts(Collections.singletonList(
ManageDataLauncherActivity.SITE_SETTINGS_SHORTCUT_ID));
return;
}
ShortcutInfo shortcut = createSiteSettingsShortcutInfo(
context, params.getStartUrl(), params.getHostBrowserPackageName());
shortcutManager.addDynamicShortcuts(Collections.singletonList(shortcut));
}
}
...@@ -167,6 +167,15 @@ ...@@ -167,6 +167,15 @@
</translations> </translations>
<release allow_pseudo="false" seq="1"> <release allow_pseudo="false" seq="1">
<messages fallback_to_english="true"> <messages fallback_to_english="true">
<message name="IDS_SITE_SETTINGS_LONG_LABEL" desc="Site settings Android app shortcut title to display on devices with larger screens. (ideally less than 25 characters)">
Manage website settings
</message>
<message name="IDS_SITE_SETTINGS_SHORT_LABEL" desc="Site settings Android app shortcut title. (ideally less than 10 characters)">
Site settings
</message>
<message name="IDS_NO_SUPPORT_FOR_MANAGE_SPACE" desc="Text to show in a toast when a user clicks on a site settings shortcut but managing space is not suported by the browser.">
Managing space not supported by: <ph name="BROWSER_PACKAGE">%1$s<ex>org.chromium.chrome</ex></ph>.
</message>
<!-- Select host browser dialog --> <!-- Select host browser dialog -->
<message name="IDS_CHOOSE_HOST_BROWSER_DIALOG_TITLE" desc="Title for the host browser picker dialog, which is used to ask users to pick a browser to launch the installed WebAPK."> <message name="IDS_CHOOSE_HOST_BROWSER_DIALOG_TITLE" desc="Title for the host browser picker dialog, which is used to ask users to pick a browser to launch the installed WebAPK.">
<ph name="APP_NAME">%1$s<ex>Progressive Web Apps</ex></ph> requires a web browser <ph name="APP_NAME">%1$s<ex>Progressive Web Apps</ex></ph> requires a web browser
......
27bfe4a6c6d5159b982fb1246904e72f9149db09
\ No newline at end of file
ac7345c9eee8154458660df14df05782b2678ba8
\ No newline at end of file
ac7345c9eee8154458660df14df05782b2678ba8
\ No newline at end of file
...@@ -194,15 +194,15 @@ public class WebApkValidator { ...@@ -194,15 +194,15 @@ public class WebApkValidator {
} }
return false; return false;
} }
if (isNotWebApkQuick(packageInfo)) {
return false;
}
if (sOverrideValidationForTesting) { if (sOverrideValidationForTesting) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Ok! Looks like a WebApk (has start url) and validation is disabled."); Log.d(TAG, "WebApk validation is disabled for testing.");
} }
return true; return true;
} }
if (isNotWebApkQuick(packageInfo)) {
return false;
}
if (verifyV1WebApk(packageInfo, webappPackageName)) { if (verifyV1WebApk(packageInfo, webappPackageName)) {
return true; return true;
} }
......
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