summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matt Casey <mrcasey@google.com> 2025-03-04 08:18:59 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-04 08:18:59 -0800
commita17dc87c00f15cacebbbf250d949818c09ba8c94 (patch)
tree1241ef8afb080a4c0e7347e89d6cabe7d13d3d30
parent73f896b82be7090ba0110ab8415c9f3a472ca1b3 (diff)
parent8324973d42d277212e7829d50ff8382de5c20553 (diff)
Merge "Fork clipboard overlay's IntentCreator behind a flag." into main
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt150
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java)22
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java14
8 files changed, 402 insertions, 110 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
new file mode 100644
index 000000000000..239e02640908
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ComponentName
+import android.content.Intent
+import android.net.Uri
+import android.text.SpannableString
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ActionIntentCreatorTest : SysuiTestCase() {
+ val creator = ActionIntentCreator()
+
+ @Test
+ fun test_getTextEditorIntent() {
+ val intent = creator.getTextEditorIntent(context)
+ assertEquals(ComponentName(context, EditTextActivity::class.java), intent.component)
+ assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+
+ @Test
+ fun test_getRemoteCopyIntent() {
+ context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "")
+
+ val clipData = ClipData.newPlainText("Test", "Test Item")
+ var intent = creator.getRemoteCopyIntent(clipData, context)
+
+ assertEquals(null, intent.component)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ assertEquals(clipData, intent.clipData)
+
+ // Try again with a remote copy component
+ val fakeComponent =
+ ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_remoteCopyPackage, fakeComponent.flattenToString())
+
+ intent = creator.getRemoteCopyIntent(clipData, context)
+ assertEquals(fakeComponent, intent.component)
+ }
+
+ @Test
+ fun test_getImageEditIntent() {
+ context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "")
+ val fakeUri = Uri.parse("content://foo")
+ var intent = creator.getImageEditIntent(fakeUri, context)
+
+ assertEquals(Intent.ACTION_EDIT, intent.action)
+ assertEquals("image/*", intent.type)
+ assertEquals(null, intent.component)
+ assertEquals("clipboard", intent.getStringExtra("edit_source"))
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+
+ // try again with an editor component
+ val fakeComponent =
+ ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity")
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString())
+ intent = creator.getImageEditIntent(fakeUri, context)
+ assertEquals(fakeComponent, intent.component)
+ }
+
+ @Test
+ fun test_getShareIntent_plaintext() {
+ val clipData = ClipData.newPlainText("Test", "Test Item")
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_html() {
+ val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>")
+ val intent = creator.getShareIntent(clipData, getContext())
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Some HTML", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_image() {
+ val uri = Uri.parse("content://something")
+ val clipData = ClipData("Test", arrayOf("image/png"), ClipData.Item(uri))
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals(uri, target?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))
+ assertEquals(uri, target?.clipData?.getItemAt(0)?.uri)
+ assertEquals("image/png", target?.type)
+ }
+
+ @Test
+ fun test_getShareIntent_spannableText() {
+ val clipData = ClipData.newPlainText("Test", SpannableString("Test Item"))
+ val intent = creator.getShareIntent(clipData, context)
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.action)
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS)
+ val target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
+ assertEquals("Test Item", target?.getStringExtra(Intent.EXTRA_TEXT))
+ assertEquals("text/plain", target?.type)
+ }
+
+ // Assert that the given flags are set
+ private fun assertFlags(intent: Intent, flags: Int) {
+ assertTrue((intent.flags and flags) == flags)
+ }
+
+ companion object {
+ private const val EXTERNAL_INTENT_FLAGS: Int =
+ (Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TASK or
+ Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
index ea6cb3b6d178..126b3fa9e7ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java
@@ -36,13 +36,15 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class IntentCreatorTest extends SysuiTestCase {
+public class DefaultIntentCreatorTest extends SysuiTestCase {
private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator();
+
@Test
public void test_getTextEditorIntent() {
- Intent intent = IntentCreator.getTextEditorIntent(getContext());
+ Intent intent = mIntentCreator.getTextEditorIntent(getContext());
assertEquals(new ComponentName(getContext(), EditTextActivity.class),
intent.getComponent());
assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -54,7 +56,7 @@ public class IntentCreatorTest extends SysuiTestCase {
"");
ClipData clipData = ClipData.newPlainText("Test", "Test Item");
- Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());
assertEquals(null, intent.getComponent());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -66,7 +68,7 @@ public class IntentCreatorTest extends SysuiTestCase {
getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
fakeComponent.flattenToString());
- intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext());
assertEquals(fakeComponent, intent.getComponent());
}
@@ -75,7 +77,7 @@ public class IntentCreatorTest extends SysuiTestCase {
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
"");
Uri fakeUri = Uri.parse("content://foo");
- Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ Intent intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
assertEquals(Intent.ACTION_EDIT, intent.getAction());
assertEquals("image/*", intent.getType());
@@ -88,14 +90,14 @@ public class IntentCreatorTest extends SysuiTestCase {
"com.android.remotecopy.RemoteCopyActivity");
getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
fakeComponent.flattenToString());
- intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ intent = mIntentCreator.getImageEditIntent(fakeUri, getContext());
assertEquals(fakeComponent, intent.getComponent());
}
@Test
public void test_getShareIntent_plaintext() {
ClipData clipData = ClipData.newPlainText("Test", "Test Item");
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -108,7 +110,7 @@ public class IntentCreatorTest extends SysuiTestCase {
public void test_getShareIntent_html() {
ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
"<b>Some HTML</b>");
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -122,7 +124,7 @@ public class IntentCreatorTest extends SysuiTestCase {
Uri uri = Uri.parse("content://something");
ClipData clipData = new ClipData("Test", new String[]{"image/png"},
new ClipData.Item(uri));
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
@@ -135,7 +137,7 @@ public class IntentCreatorTest extends SysuiTestCase {
@Test
public void test_getShareIntent_spannableText() {
ClipData clipData = ClipData.newPlainText("Test", new SpannableString("Test Item"));
- Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+ Intent intent = mIntentCreator.getShareIntent(clipData, getContext());
assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
assertFlags(intent, EXTERNAL_INTENT_FLAGS);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
new file mode 100644
index 000000000000..df6c1b18e3e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay
+
+import android.content.ClipData
+import android.content.ClipDescription
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.text.TextUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class ActionIntentCreator @Inject constructor() : IntentCreator {
+ override fun getTextEditorIntent(context: Context?) =
+ Intent(context, EditTextActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+
+ override fun getShareIntent(clipData: ClipData, context: Context?): Intent {
+ val shareIntent = Intent(Intent.ACTION_SEND)
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ val uri = clipData.getItemAt(0).uri
+ shareIntent.apply {
+ if (uri != null) {
+ // We don't use setData here because some apps interpret this as "to:".
+ setType(clipData.description.getMimeType(0))
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ setClipData(
+ ClipData(
+ ClipDescription("content", arrayOf(clipData.description.getMimeType(0))),
+ ClipData.Item(uri),
+ )
+ )
+ putExtra(Intent.EXTRA_STREAM, uri)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ } else {
+ putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString())
+ setType("text/plain")
+ }
+ }
+
+ return Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+
+ override fun getImageEditIntent(uri: Uri?, context: Context): Intent {
+ val editorPackage = context.getString(R.string.config_screenshotEditor)
+ return Intent(Intent.ACTION_EDIT).apply {
+ if (!TextUtils.isEmpty(editorPackage)) {
+ setComponent(ComponentName.unflattenFromString(editorPackage))
+ }
+ setDataAndType(uri, "image/*")
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD)
+ }
+ }
+
+ override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent {
+ val remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage)
+ return Intent(REMOTE_COPY_ACTION).apply {
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ setComponent(ComponentName.unflattenFromString(remoteCopyPackage))
+ }
+
+ setClipData(clipData)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ }
+
+ companion object {
+ private const val EXTRA_EDIT_SOURCE: String = "edit_source"
+ private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard"
+ private const val REMOTE_COPY_ACTION: String = "android.intent.action.REMOTE_COPY"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index ac747845267c..314b6e7f5a28 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -65,7 +65,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.TimeoutHandler;
@@ -94,13 +93,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private final ClipboardOverlayWindow mWindow;
private final TimeoutHandler mTimeoutHandler;
private final ClipboardOverlayUtils mClipboardUtils;
- private final FeatureFlags mFeatureFlags;
private final Executor mBgExecutor;
private final ClipboardImageLoader mClipboardImageLoader;
private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
private final ClipboardIndicationProvider mClipboardIndicationProvider;
+ private final IntentCreator mIntentCreator;
private Runnable mOnSessionCompleteListener;
private Runnable mOnRemoteCopyTapped;
@@ -189,13 +188,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
BroadcastDispatcher broadcastDispatcher,
BroadcastSender broadcastSender,
TimeoutHandler timeoutHandler,
- FeatureFlags featureFlags,
ClipboardOverlayUtils clipboardUtils,
@Background Executor bgExecutor,
ClipboardImageLoader clipboardImageLoader,
ClipboardTransitionExecutor transitionExecutor,
ClipboardIndicationProvider clipboardIndicationProvider,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ IntentCreator intentCreator) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mClipboardImageLoader = clipboardImageLoader;
@@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
mClipboardIndicationProvider = clipboardIndicationProvider;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
+ mIntentCreator = intentCreator;
mView = clipboardOverlayView;
mWindow = clipboardOverlayWindow;
@@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
hideImmediate();
});
- mFeatureFlags = featureFlags;
mTimeoutHandler = timeoutHandler;
mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
@@ -508,7 +507,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
}
private void maybeShowRemoteCopy(ClipData clipData) {
- Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
+ Intent remoteCopyIntent = mIntentCreator.getRemoteCopyIntent(clipData, mContext);
+
// Only show remote copy if it's available.
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.resolveActivity(
@@ -558,19 +558,19 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
+ mContext.startActivity(mIntentCreator.getImageEditIntent(uri, mContext));
animateOut();
}
private void editText() {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
+ mContext.startActivity(mIntentCreator.getTextEditorIntent(mContext));
animateOut();
}
private void shareContent(ClipData clip) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
- mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
+ mContext.startActivity(mIntentCreator.getShareIntent(clip, mContext));
animateOut();
}
@@ -717,22 +717,22 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
public void onRemoteCopyButtonTapped() {
if (clipboardSharedTransitions()) {
finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED,
- IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
+ mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
}
}
@Override
public void onShareButtonTapped() {
if (clipboardSharedTransitions()) {
+ Intent shareIntent =
+ mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext);
switch (mClipboardModel.getType()) {
case TEXT:
case URI:
- finish(CLIPBOARD_OVERLAY_SHARE_TAPPED,
- IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
+ finish(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
break;
case IMAGE:
- finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED,
- IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
+ finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, shareIntent);
break;
}
}
@@ -744,11 +744,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
switch (mClipboardModel.getType()) {
case TEXT:
finish(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- IntentCreator.getTextEditorIntent(mContext));
+ mIntentCreator.getTextEditorIntent(mContext));
break;
case IMAGE:
finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED,
- IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
+ mIntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext));
break;
default:
Log.w(TAG, "Got preview tapped callback for non-editable type "
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
new file mode 100644
index 000000000000..4b24536ad28f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
+
+import javax.inject.Inject;
+
+@SysUISingleton
+public class DefaultIntentCreator implements IntentCreator {
+ private static final String EXTRA_EDIT_SOURCE = "edit_source";
+ private static final String EDIT_SOURCE_CLIPBOARD = "clipboard";
+ private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+ @Inject
+ public DefaultIntentCreator() {}
+
+ public Intent getTextEditorIntent(Context context) {
+ Intent intent = new Intent(context, EditTextActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
+ }
+
+ public Intent getShareIntent(ClipData clipData, Context context) {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ Uri uri = clipData.getItemAt(0).getUri();
+ if (uri != null) {
+ // We don't use setData here because some apps interpret this as "to:".
+ shareIntent.setType(clipData.getDescription().getMimeType(0));
+ // Include URI in ClipData also, so that grantPermission picks it up.
+ shareIntent.setClipData(new ClipData(
+ new ClipDescription(
+ "content", new String[]{clipData.getDescription().getMimeType(0)}),
+ new ClipData.Item(uri)));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(
+ Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString());
+ shareIntent.setType("text/plain");
+ }
+ Intent chooserIntent = Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ return chooserIntent;
+ }
+
+ public Intent getImageEditIntent(Uri uri, Context context) {
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setDataAndType(uri, "image/*");
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
+ return editIntent;
+ }
+
+ public Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+
+ String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
+ }
+
+ nearbyIntent.setClipData(clipData);
+ nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return nearbyIntent;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
index a18b4c84b081..c8a6b05f090b 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,79 +17,13 @@
package com.android.systemui.clipboardoverlay;
import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.text.TextUtils;
-import com.android.systemui.res.R;
-
-class IntentCreator {
- private static final String EXTRA_EDIT_SOURCE = "edit_source";
- private static final String EDIT_SOURCE_CLIPBOARD = "clipboard";
- private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
-
- static Intent getTextEditorIntent(Context context) {
- Intent intent = new Intent(context, EditTextActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return intent;
- }
-
- static Intent getShareIntent(ClipData clipData, Context context) {
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
-
- // From the ACTION_SEND docs:
- // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
- // MIME type of the data in EXTRA_STREAM"
- Uri uri = clipData.getItemAt(0).getUri();
- if (uri != null) {
- // We don't use setData here because some apps interpret this as "to:".
- shareIntent.setType(clipData.getDescription().getMimeType(0));
- // Include URI in ClipData also, so that grantPermission picks it up.
- shareIntent.setClipData(new ClipData(
- new ClipDescription(
- "content", new String[]{clipData.getDescription().getMimeType(0)}),
- new ClipData.Item(uri)));
- shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else {
- shareIntent.putExtra(
- Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context).toString());
- shareIntent.setType("text/plain");
- }
- Intent chooserIntent = Intent.createChooser(shareIntent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- return chooserIntent;
- }
-
- static Intent getImageEditIntent(Uri uri, Context context) {
- String editorPackage = context.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setDataAndType(uri, "image/*");
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- editIntent.putExtra(EXTRA_EDIT_SOURCE, EDIT_SOURCE_CLIPBOARD);
- return editIntent;
- }
-
- static Intent getRemoteCopyIntent(ClipData clipData, Context context) {
- Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
-
- String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
- if (!TextUtils.isEmpty(remoteCopyPackage)) {
- nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
- }
-
- nearbyIntent.setClipData(clipData);
- nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return nearbyIntent;
- }
+public interface IntentCreator {
+ Intent getTextEditorIntent(Context context);
+ Intent getShareIntent(ClipData clipData, Context context);
+ Intent getImageEditIntent(Uri uri, Context context);
+ Intent getRemoteCopyIntent(ClipData clipData, Context context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index 6c10eea07ffc..c86a84b17efe 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static com.android.systemui.Flags.clipboardOverlayMultiuser;
import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.shared.Flags.usePreferredImageEditor;
import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -32,7 +33,10 @@ import android.view.WindowManager;
import com.android.app.viewcapture.ViewCapture;
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.systemui.clipboardoverlay.ActionIntentCreator;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
+import com.android.systemui.clipboardoverlay.DefaultIntentCreator;
+import com.android.systemui.clipboardoverlay.IntentCreator;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -102,6 +106,17 @@ public interface ClipboardOverlayModule {
/* isViewCaptureEnabled= */ enableViewCaptureTracing());
}
+ @Provides
+ static IntentCreator provideIntentCreator(
+ Lazy<DefaultIntentCreator> defaultIntentCreator,
+ Lazy<ActionIntentCreator> actionIntentCreator) {
+ if (usePreferredImageEditor()) {
+ return actionIntentCreator.get();
+ } else {
+ return defaultIntentCreator.get();
+ }
+ }
+
@Qualifier
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 562481567536..5c893da45b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -28,7 +28,6 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -60,7 +59,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -102,7 +100,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Mock
private UiEventLogger mUiEventLogger;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private Animator mAnimator;
@@ -152,8 +149,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
new ClipData.Item("Test Item"));
-
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests
}
/**
@@ -170,13 +165,13 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
getFakeBroadcastDispatcher(),
mBroadcastSender,
mTimeoutHandler,
- mFeatureFlags,
mClipboardUtils,
mExecutor,
mClipboardImageLoader,
mClipboardTransitionExecutor,
mClipboardIndicationProvider,
- mUiEventLogger);
+ mUiEventLogger,
+ new ActionIntentCreator());
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
}
@@ -193,7 +188,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
ClipData clipData = new ClipData("", new String[]{"image/png"},
new ClipData.Item(Uri.parse("")));
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
mOverlayController.setClipData(clipData, "");
@@ -208,7 +202,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
ClipData clipData = new ClipData("", new String[]{"resource/png"},
new ClipData.Item(Uri.parse("")));
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
mOverlayController.setClipData(clipData, "");
@@ -219,7 +212,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_textData_legacy() {
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
mOverlayController.setClipData(mSampleClipData, "abc");
@@ -232,7 +224,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_sensitiveTextData_legacy() {
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
ClipDescription description = mSampleClipData.getDescription();
@@ -250,7 +241,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
@Test
public void test_setClipData_repeatedCalls_legacy() {
when(mAnimator.isRunning()).thenReturn(true);
- mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false);
initController();
mOverlayController.setClipData(mSampleClipData, "");