Commit 6460aa02 authored by Ella Ge's avatar Ella Ge Committed by Commit Bot

Merge Webapk/TWA disclosure controller 1

This CL moves WebappDisclosureSnackbarController to browserservices
folder, makes it use TrustedWebActivityModel and the
DisclosureInfobar as TrustedWebActivityDisclosureController.

This will change the text in unbound webapk's disclosure snackbar text
to be the same as TWA one.

Bug: 1128675
Change-Id: I900c41c04a6f36489bed1e08cf81c74f89e2801e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2453896Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Commit-Queue: Ella Ge <eirage@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817516}
parent 4dad5674
......@@ -218,6 +218,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifier.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/AddToHomescreenVerifier.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebApkVerifier.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureController.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappVerifier.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashController.java",
"java/src/org/chromium/chrome/browser/browserservices/ui/splashscreen/SplashDelegate.java",
......@@ -1679,7 +1680,6 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/webapps/WebappDataStorage.java",
"java/src/org/chromium/chrome/browser/webapps/WebappDeferredStartupWithStorageHandler.java",
"java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java",
"java/src/org/chromium/chrome/browser/webapps/WebappDisclosureSnackbarController.java",
"java/src/org/chromium/chrome/browser/webapps/WebappExtras.java",
"java/src/org/chromium/chrome/browser/webapps/WebappIcon.java",
"java/src/org/chromium/chrome/browser/webapps/WebappInfo.java",
......
......@@ -33,6 +33,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityDisclosureControllerTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TrustedWebActivityOpenTimeRecorderTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/controller/trustedwebactivity/TwaVerifierTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/controller/webapps/WebappDisclosureControllerTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/trustedwebactivity/DisclosureAcceptanceBroadcastReceiverTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/trustedwebactivity/DisclosureUiPickerTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ui/trustedwebactivity/FilledLazy.java",
......@@ -250,7 +251,6 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappDataStorageTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappDisclosureSnackbarControllerTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappInfoTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappIntentUtilsTest.java",
"junit/src/org/chromium/chrome/browser/webapps/WebappLauncherActivityTest.java",
......
......@@ -413,7 +413,7 @@ specific_include_rules = {
"WebappDeferredStartupWithStorageHandler\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
],
"WebappDisclosureSnackbarController\.java": [
"WebappDisclosureController\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
],
"AddToHomescreenCoordinator\.java": [
......
......@@ -2,18 +2,27 @@
// 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.webapps;
package org.chromium.chrome.browser.browserservices.ui.controller.webapps;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.PACKAGE_NAME;
import androidx.annotation.Nullable;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.lifecycle.NativeInitObserver;
import org.chromium.chrome.browser.webapps.WebApkExtras;
import org.chromium.chrome.browser.webapps.WebappDataStorage;
import org.chromium.chrome.browser.webapps.WebappDeferredStartupWithStorageHandler;
import org.chromium.chrome.browser.webapps.WebappExtras;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.components.webapk.lib.common.WebApkConstants;
import javax.inject.Inject;
......@@ -28,18 +37,24 @@ import javax.inject.Inject;
* next time the app is opened if it hasn't been acknowledged.
*/
@ActivityScope
public class WebappDisclosureSnackbarController
implements SnackbarManager.SnackbarController, PauseResumeWithNativeObserver {
private final ChromeActivity mActivity;
public class WebappDisclosureController
implements NativeInitObserver, TrustedWebActivityModel.DisclosureEventsCallback {
private final BrowserServicesIntentDataProvider mIntentDataProvider;
private final TrustedWebActivityModel mModel;
@Inject
public WebappDisclosureSnackbarController(ChromeActivity<?> activity,
BrowserServicesIntentDataProvider intentDataProvider,
public WebappDisclosureController(ChromeActivity<?> activity,
BrowserServicesIntentDataProvider intentDataProvider, TrustedWebActivityModel model,
WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
ActivityLifecycleDispatcher lifecycleDispatcher) {
mActivity = activity;
mIntentDataProvider = intentDataProvider;
mModel = model;
model.set(DISCLOSURE_EVENTS_CALLBACK, this);
WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
if (webApkExtras != null && webApkExtras.webApkPackageName != null) {
model.set(PACKAGE_NAME, webApkExtras.webApkPackageName);
}
lifecycleDispatcher.register(this);
......@@ -62,7 +77,7 @@ public class WebappDisclosureSnackbarController
}
@Override
public void onResumeWithNative() {
public void onFinishNativeInitialization() {
WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(
mIntentDataProvider.getWebappExtras().id);
......@@ -72,23 +87,18 @@ public class WebappDisclosureSnackbarController
}
@Override
public void onPauseWithNative() {}
/**
* @param actionData an instance of WebappInfo
*/
@Override
public void onAction(Object actionData) {
if (actionData instanceof WebappDataStorage) {
((WebappDataStorage) actionData).clearShowDisclosure();
public void onDisclosureAccepted() {
WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
WebappDataStorage storage = WebappRegistry.getInstance().getWebappDataStorage(
mIntentDataProvider.getWebappExtras().id);
if (storage != null) {
storage.clearShowDisclosure();
}
mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_DISMISSED_BY_USER);
}
/**
* Stub expected by SnackbarController.
*/
@Override
public void onDismissNoAction(Object actionData) {}
public void onDisclosureShown() {}
/**
* Shows the disclosure informing the user the Webapp is running in Chrome.
......@@ -101,14 +111,8 @@ public class WebappDisclosureSnackbarController
// If forced we set the bit to show the disclosure. This persists to future instances.
if (force) storage.setShowDisclosure();
if (shouldShowDisclosure(storage)) {
mActivity.getSnackbarManager().showSnackbar(
Snackbar.make(mActivity.getResources().getString(
R.string.app_running_in_chrome_disclosure),
this, Snackbar.TYPE_PERSISTENT,
Snackbar.UMA_WEBAPK_PRIVACY_DISCLOSURE)
.setAction(mActivity.getResources().getString(R.string.ok), storage)
.setSingleLine(false));
if (!isShowing() && shouldShowDisclosure(storage)) {
mModel.set(DISCLOSURE_STATE, DISCLOSURE_STATE_SHOWN);
}
}
......@@ -129,4 +133,8 @@ public class WebappDisclosureSnackbarController
&& !webApkExtras.webApkPackageName.startsWith(
WebApkConstants.WEBAPK_PACKAGE_PREFIX);
}
public boolean isShowing() {
return mModel.get(DISCLOSURE_STATE) == DISCLOSURE_STATE_SHOWN;
}
}
......@@ -7,6 +7,8 @@ package org.chromium.chrome.browser.webapps;
import androidx.annotation.Nullable;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.browserservices.ui.controller.webapps.WebappDisclosureController;
import org.chromium.chrome.browser.browserservices.ui.view.trustedwebactivity.DisclosureInfobar;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.Destroyable;
......@@ -27,11 +29,11 @@ public class WebApkActivityCoordinator implements Destroyable {
@Inject
public WebApkActivityCoordinator(ChromeActivity<?> activity,
WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
WebappDisclosureSnackbarController disclosureSnackbarController,
WebappDisclosureController disclosureController, DisclosureInfobar disclosureInfobar,
WebApkActivityLifecycleUmaTracker webApkActivityLifecycleUmaTracker,
ActivityLifecycleDispatcher lifecycleDispatcher,
Lazy<WebApkUpdateManager> webApkUpdateManager) {
// We don't need to do anything with |disclosureSnackbarController| and
// We don't need to do anything with |disclosureController|, |disclosureInfobar| and
// |webApkActivityLifecycleUmaTracker|. We just need to resolve
// them so that they start working.
......
......@@ -304,7 +304,7 @@ public class WebappDataStorage {
* Deletes the data for a web app by clearing all the information inside the SharedPreferences
* file. This does NOT delete the file itself but the file is left empty.
*/
void delete() {
public void delete() {
deletePendingUpdateRequestFile();
mPreferences.edit().clear().apply();
}
......@@ -455,7 +455,7 @@ public class WebappDataStorage {
* Returns whether to show the user a privacy disclosure (used for TWAs and unbound WebAPKs).
* This is not cleared until the user explicitly acknowledges it.
*/
boolean shouldShowDisclosure() {
public boolean shouldShowDisclosure() {
return mPreferences.getBoolean(KEY_SHOW_DISCLOSURE, false);
}
......@@ -464,7 +464,7 @@ public class WebappDataStorage {
* disclosure on every resume of the Webapp. This should be called when the user has
* acknowledged the disclosure.
*/
void clearShowDisclosure() {
public void clearShowDisclosure() {
mPreferences.edit().putBoolean(KEY_SHOW_DISCLOSURE, false).apply();
}
......@@ -473,7 +473,7 @@ public class WebappDataStorage {
* This is set the first time an app is opened without storage (either right after install or
* after Chrome's storage is cleared).
*/
void setShowDisclosure() {
public void setShowDisclosure() {
mPreferences.edit().putBoolean(KEY_SHOW_DISCLOSURE, true).apply();
}
......
......@@ -23,7 +23,10 @@ import javax.inject.Inject;
*/
@ActivityScope
public class WebappDeferredStartupWithStorageHandler {
interface Task {
/**
* Interface for deferred startup task callbacks.
*/
public interface Task {
/**
* Called to run task.
* @param storage Null if there is no {@link WebappDataStorage} registered for the webapp
......
......@@ -18,6 +18,7 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.browserservices.ui.SharedActivityCoordinator;
import org.chromium.chrome.browser.browserservices.ui.controller.webapps.WebappDisclosureController;
import org.chromium.chrome.browser.customtabs.CustomTabOrientationController;
import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
import org.chromium.chrome.browser.dependency_injection.ModuleOverridesRule;
......@@ -134,8 +135,8 @@ public class WebApkInitializationTest {
mTrackingActivityLifecycleDispatcher.getRegisteredObserverClassNames();
assertTrue(registeredObserverClassNames.contains(
WebappActionsNotificationManager.class.getName()));
assertTrue(registeredObserverClassNames.contains(
WebappDisclosureSnackbarController.class.getName()));
assertTrue(
registeredObserverClassNames.contains(WebappDisclosureController.class.getName()));
assertTrue(registeredObserverClassNames.contains(
WebApkActivityLifecycleUmaTracker.class.getName()));
assertTrue(
......
......@@ -2,19 +2,20 @@
// 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.webapps;
package org.chromium.chrome.browser.browserservices.ui.controller.webapps;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.res.Resources;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_EVENTS_CALLBACK;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_DISMISSED_BY_USER;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_NOT_SHOWN;
import static org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel.DISCLOSURE_STATE_SHOWN;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -27,41 +28,45 @@ import org.chromium.base.task.PostTask;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.ui.trustedwebactivity.TrustedWebActivityModel;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.webapps.WebappActivity;
import org.chromium.chrome.browser.webapps.WebappDataStorage;
import org.chromium.chrome.browser.webapps.WebappDeferredStartupWithStorageHandler;
import org.chromium.chrome.browser.webapps.WebappIntentUtils;
import org.chromium.chrome.browser.webapps.WebappRegistry;
import org.chromium.chrome.test.util.browser.webapps.WebApkIntentDataProviderBuilder;
import org.chromium.components.webapk.lib.common.WebApkConstants;
/**
* Tests for WebappDisclosureSnackbarController
* Tests for WebappDisclosureController
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class WebappDisclosureSnackbarControllerTest {
public class WebappDisclosureControllerTest {
@Mock
public WebappActivity mActivity;
@Mock
public SnackbarManager mManager;
@Mock
public Resources mResources;
public TrustedWebActivityModel mModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
// Run AsyncTasks synchronously.
PostTask.setPrenativeThreadPoolExecutorForTesting(new RoboExecutorService());
}
doReturn("test text").when(mResources).getString(anyInt());
doReturn(mManager).when(mActivity).getSnackbarManager();
doReturn(mResources).when(mActivity).getResources();
@After
public void tearDown() {
PostTask.resetPrenativeThreadPoolExecutorForTesting();
}
private WebappDisclosureSnackbarController buildControllerForWebApk(String webApkPackageName) {
private WebappDisclosureController buildControllerForWebApk(String webApkPackageName) {
BrowserServicesIntentDataProvider intentDataProvider =
new WebApkIntentDataProviderBuilder(webApkPackageName, "https://pwa.rocks/")
.build();
return new WebappDisclosureSnackbarController(mActivity, intentDataProvider,
mModel = new TrustedWebActivityModel();
return new WebappDisclosureController(mActivity, intentDataProvider, mModel,
mock(WebappDeferredStartupWithStorageHandler.class),
mock(ActivityLifecycleDispatcher.class));
}
......@@ -73,57 +78,80 @@ public class WebappDisclosureSnackbarControllerTest {
}
public void verifyShownThenDismissedOnNewCreateStorage(String packageName) {
WebappDisclosureSnackbarController controller = buildControllerForWebApk(packageName);
WebappDisclosureController controller = buildControllerForWebApk(packageName);
WebappDataStorage storage = registerStorageForWebApk(packageName);
// Simulates the case that shows the disclosure when creating a new storage.
controller.onDeferredStartupWithStorage(storage, true /* didCreateStorage */);
verify(mManager, times(1)).showSnackbar(any(Snackbar.class));
assertTrue(storage.shouldShowDisclosure());
assertSnackbarShown();
// Simulate a restart or a resume.
controller.onResumeWithNative();
verify(mManager, times(2)).showSnackbar(any(Snackbar.class));
assertTrue(storage.shouldShowDisclosure());
// Dismiss the disclosure.
mModel.get(DISCLOSURE_EVENTS_CALLBACK).onDisclosureAccepted();
assertSnackbarAccepted();
assertFalse(storage.shouldShowDisclosure());
storage.delete();
}
public void verifyShownThenDismissedOnRestart(String packageName) {
WebappDisclosureController controller = buildControllerForWebApk(packageName);
WebappDataStorage storage = registerStorageForWebApk(packageName);
// Simulates the case that shows the disclosure when finish native initialization.
storage.setShowDisclosure();
controller.onFinishNativeInitialization();
assertSnackbarShown();
// Dismiss the disclosure.
controller.onAction(storage);
mModel.get(DISCLOSURE_EVENTS_CALLBACK).onDisclosureAccepted();
// Simulate resuming or starting again this time no disclosure should show.
assertSnackbarAccepted();
assertFalse(storage.shouldShowDisclosure());
controller.onResumeWithNative();
verify(mManager, times(2)).showSnackbar(any(Snackbar.class));
storage.delete();
}
public void verifyNotShownOnExistingStorageWithoutShouldShowDisclosure(String packageName) {
WebappDisclosureSnackbarController controller = buildControllerForWebApk(packageName);
WebappDisclosureController controller = buildControllerForWebApk(packageName);
WebappDataStorage storage = registerStorageForWebApk(packageName);
// Simulate that starting with existing storage will not cause the disclosure to show.
assertFalse(storage.shouldShowDisclosure());
controller.onDeferredStartupWithStorage(storage, false /* didCreateStorage */);
verify(mManager, times(0)).showSnackbar(any(Snackbar.class));
assertSnackbarNotShown();
storage.delete();
}
public void verifyNeverShown(String packageName) {
WebappDisclosureSnackbarController controller = buildControllerForWebApk(packageName);
WebappDisclosureController controller = buildControllerForWebApk(packageName);
WebappDataStorage storage = registerStorageForWebApk(packageName);
// Try to show the disclosure the first time.
controller.onDeferredStartupWithStorage(storage, true /* didCreateStorage */);
verify(mManager, times(0)).showSnackbar(any(Snackbar.class));
assertSnackbarNotShown();
// Try to the disclosure again this time emulating a restart or a resume.
controller.onResumeWithNative();
verify(mManager, times(0)).showSnackbar(any(Snackbar.class));
// Try to the disclosure again this time emulating a restart.
controller.onFinishNativeInitialization();
assertSnackbarNotShown();
storage.delete();
}
private void assertSnackbarShown() {
assertEquals(DISCLOSURE_STATE_SHOWN, mModel.get(DISCLOSURE_STATE));
}
private void assertSnackbarAccepted() {
assertEquals(DISCLOSURE_STATE_DISMISSED_BY_USER, mModel.get(DISCLOSURE_STATE));
}
private void assertSnackbarNotShown() {
assertEquals(DISCLOSURE_STATE_NOT_SHOWN, mModel.get(DISCLOSURE_STATE));
}
@Test
@Feature({"Webapps"})
public void testUnboundWebApkShowDisclosure() {
......@@ -131,6 +159,13 @@ public class WebappDisclosureSnackbarControllerTest {
verifyShownThenDismissedOnNewCreateStorage(packageName);
}
@Test
@Feature({"Webapps"})
public void testUnboundWebApkShowDisclosure2() {
String packageName = "unbound";
verifyShownThenDismissedOnRestart(packageName);
}
@Test
@Feature({"Webapps"})
public void testUnboundWebApkNoDisclosureOnExistingStorage() {
......
......@@ -3508,9 +3508,6 @@ Data from your Incognito session will only be cleared from Chrome when you <ph n
</message>
<!-- WebAPK/TWA related strings -->
<message name="IDS_APP_RUNNING_IN_CHROME_DISCLOSURE" desc="Message on the snackbar that indicates an app may use Chrome data.">
This app is running in Chrome
</message>
<message name="IDS_TWA_RUNNING_IN_CHROME" desc="Message on a snackbar indicating that the current Activity may use Chrome data (the rest of the app may not be).">
Running in Chrome
</message>
......
c148a3b6564ccb24297b4fbd58ba2ad983a11985
\ No newline at end of file
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