[Share] Use cleaner intent to remove top ChooserActivity
The original CL cause a regression for CCT, as sending intent to itself creates a new CCT session. Based on the original CL, adding an intent extra so when activities receive the new intent, it'll be dropped properly.
Recording: https://drive.google.com/file/d/1AZMhz-QWxQLuSZr8Tk-mFtGYK59aUzSL/view?usp=drive_link&resourcekey=0-ZffGjQClTpHLwlyLcp7mgw
(Description from original CL: crrev.com/c/4750566)
[Share] Remove TargetChosenReceiver when WindowAndroid destroyed
Android share sheet is showing with as ChooserActivity on top of Chrome. When theme switches, Chrome activity is recreated, so the previous stored custom actions should go away since they are referencing the old activity. As a result, the custom actions on the share sheet becomes no-op once selected.
This CL include the fix that:
* Remove the weak reference to Activity and WindowAndroid once it is removed from the UserDataHost (in WindowAndroid)
* Close the share sheet. This is done by sending an intent with Intent.FLAG_ACTIVITY_CLEAR_TOP targeting to the dispatching activity.
Bug: 1470978, 1470036
Change-Id: I6c9efb35f1e77c92e78bfbe6a3d8220419319e5d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4763472
Commit-Queue: Wenyu Fu <wenyufu@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Theresa Sullivan <twellington@chromium.org>
Reviewed-by: Jinsuk Kim <jinsukkim@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1183495}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 93db5d1..96ac124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -140,6 +140,7 @@
import org.chromium.chrome.browser.reengagement.ReengagementNotificationController;
import org.chromium.chrome.browser.search_engines.SearchEngineChoiceNotification;
import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
+import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
@@ -893,6 +894,11 @@
@Override
public void onNewIntent(Intent intent) {
try (TraceEvent e = TraceEvent.scoped("ChromeTabbedActivity.onNewIntent")) {
+ // Drop the cleaner intent since it's created in order to clear up the OS share sheet.
+ if (ShareHelper.isCleanerIntent(intent)) {
+ return;
+ }
+
// The intent to use in maybeDispatchExplicitMainViewIntent(). We're explicitly
// adding NEW_TASK flag to make sure backing from CCT brings up the caller activity,
// and not Chrome
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 0eb234ac..98f008a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -63,6 +63,7 @@
import org.chromium.chrome.browser.usage_stats.UsageStatsService;
import org.chromium.chrome.browser.webapps.SameTaskWebApkActivity;
import org.chromium.chrome.browser.webapps.WebappActivityCoordinator;
+import org.chromium.components.browser_ui.share.ShareHelper;
import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
@@ -139,6 +140,11 @@
@Override
public void onNewIntent(Intent intent) {
+ // Drop the cleaner intent since it's created in order to clear up the OS share sheet.
+ if (ShareHelper.isCleanerIntent(intent)) {
+ return;
+ }
+
Intent originalIntent = getIntent();
super.onNewIntent(intent);
// Currently we can't handle arbitrary updates of intent parameters, so make sure
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index e2fe5fb3..bb3a393 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -48,6 +48,7 @@
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
+import org.chromium.components.browser_ui.share.ShareHelper;
import org.chromium.components.browser_ui.util.FirstDrawDetector;
import org.chromium.ui.base.ActivityIntentRequestTrackerDelegate;
import org.chromium.ui.base.ActivityWindowAndroid;
@@ -574,6 +575,7 @@
@SuppressLint("MissingSuperCall") // Empty method in parent Activity class.
public void onNewIntent(Intent intent) {
if (intent == null) return;
+ if (ShareHelper.isCleanerIntent(intent)) return;
mNativeInitializationController.onNewIntent(intent);
setIntent(intent);
}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareHelperMultiInstanceUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareHelperMultiInstanceUnitTest.java
index 968fa2bb..0289b0439 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareHelperMultiInstanceUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareHelperMultiInstanceUnitTest.java
@@ -129,7 +129,7 @@
public void shareInTwoWindow_KillFirstWindowThenCompleteSecond() throws SendIntentException {
mWindowFoo.startShare();
mWindowBar.startShare();
- mWindowFoo.closeWindow();
+ mWindowFoo.closeWindow().verifyCleanerIntentDispatched();
mWindowBar.verifyCallbackNotCalled()
.completeShareWithComponent(COMPONENT_NAME_2)
.verifyCallbackState()
@@ -140,7 +140,7 @@
@Test
public void shareInTwoWindow_KillSecondWindowThenCompleteFirst() throws SendIntentException {
mWindowFoo.startShare();
- mWindowBar.startShare().closeWindow();
+ mWindowBar.startShare().closeWindow().verifyCleanerIntentDispatched();
mWindowFoo.verifyCallbackNotCalled()
.completeShareWithComponent(COMPONENT_NAME_1)
.verifyCallbackState()
@@ -240,13 +240,25 @@
return this;
}
- public void closeWindow() {
- if (mClosed) return;
+ public SingleWindowTestInstance verifyCleanerIntentDispatched() {
+ Intent intent = Shadows.shadowOf(mActivity).peekNextStartedActivity();
+ assertNotNull("Cleaner intent is not sent.", intent);
+ assertEquals("Cleaner intent does not have the right class name.",
+ intent.getComponent().getClassName(), mActivity.getClass().getName());
+ assertTrue("FLAG_ACTIVITY_CLEAR_TOP is not set for cleaner intent.",
+ (intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0);
+ return this;
+ }
+
+ public SingleWindowTestInstance closeWindow() {
+ if (mClosed) return this;
mClosed = true;
mWindow.destroy();
mActivity.finish();
mActivityScenario.close();
+
+ return this;
}
private ShareParams getTextParams() {
diff --git a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
index 2e7edfc7..e472f39 100644
--- a/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
+++ b/components/browser_ui/share/android/java/src/org/chromium/components/browser_ui/share/ShareHelper.java
@@ -33,6 +33,7 @@
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.UnownedUserData;
+import org.chromium.base.UnownedUserDataHost;
import org.chromium.base.UnownedUserDataKey;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.components.browser_ui.share.ShareParams.TargetChosenCallback;
@@ -48,6 +49,9 @@
private static final String TAG = "AndroidShare";
/** The task ID of the activity that triggered the share action. */
private static final String EXTRA_TASK_ID = "org.chromium.chrome.extra.TASK_ID";
+ /** The string identifier used as a key to mark the clean up intent. */
+ private static final String EXTRA_CLEAN_SHARE_SHEET =
+ "org.chromium.chrome.extra.CLEAN_SHARE_SHEET";
/** The string identifier used as a key to set the extra stream's alt text */
private static final String EXTRA_STREAM_ALT_TEXT = "android.intent.extra.STREAM_ALT_TEXT";
@@ -114,6 +118,17 @@
}
/**
+ * Whether the intent is a send back clean up intent. This is an workaround for Chrome to clean
+ * the top share sheet activity.
+ * @param intent newIntent received by Chrome activity.
+ * @return Whether the intent can be ignored.
+ */
+ public static boolean isCleanerIntent(Intent intent) {
+ if (!IntentUtils.isTrustedIntentFromSelf(intent)) return false;
+ return IntentUtils.safeGetBooleanExtra(intent, EXTRA_CLEAN_SHARE_SHEET, false);
+ }
+
+ /**
* Fire the intent to share content with the target app.
*
* @param window The current window.
@@ -253,6 +268,29 @@
}
}
+ @Override
+ public void onDetachedFromHost(UnownedUserDataHost host) {
+ // Remove the weak reference to the context and window when it is removed from the
+ // attaching window.
+ if (mAttachedContext.get() != null) {
+ // Issue a cleaner intent so the share sheet is cleared. This is a workaround to
+ // close the top ChooserActivity when share isn't completed.
+ Intent cleanerIntent = createCleanupIntent();
+ mAttachedContext.get().startActivity(cleanerIntent);
+ }
+ cancel();
+ }
+
+ protected Intent createCleanupIntent() {
+ Intent cleanerIntent = new Intent();
+ cleanerIntent.setClass(mAttachedContext.get(), mAttachedContext.get().getClass());
+ cleanerIntent.putExtra(EXTRA_CLEAN_SHARE_SHEET, true);
+ cleanerIntent.setFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ IntentUtils.addTrustedIntentExtras(cleanerIntent);
+ return cleanerIntent;
+ }
+
private boolean isUntrustedIntent(Intent intent) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
&& !IntentUtils.isTrustedIntentFromSelf(intent);