Loading packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * 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) } } packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java→packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java +12 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading @@ -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()); } Loading @@ -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()); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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" } } packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +16 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardIndicationProvider = clipboardIndicationProvider; mClipboardLogger = new ClipboardLogger(uiEventLogger); mIntentCreator = intentCreator; mView = clipboardOverlayView; mWindow = clipboardOverlayWindow; Loading @@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv hideImmediate(); }); mFeatureFlags = featureFlags; mTimeoutHandler = timeoutHandler; mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS); Loading Loading @@ -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( Loading Loading @@ -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(); } Loading Loading @@ -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; } } Loading @@ -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 " Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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; } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * 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) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java→packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java +12 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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); Loading @@ -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()); } Loading @@ -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()); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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" } }
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +16 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -203,6 +202,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardIndicationProvider = clipboardIndicationProvider; mClipboardLogger = new ClipboardLogger(uiEventLogger); mIntentCreator = intentCreator; mView = clipboardOverlayView; mWindow = clipboardOverlayWindow; Loading @@ -211,7 +211,6 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv hideImmediate(); }); mFeatureFlags = featureFlags; mTimeoutHandler = timeoutHandler; mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS); Loading Loading @@ -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( Loading Loading @@ -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(); } Loading Loading @@ -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; } } Loading @@ -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 " Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/DefaultIntentCreator.java 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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; } }