Loading packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt +57 −8 Original line number Diff line number Diff line Loading @@ -22,23 +22,28 @@ import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.UserInfo import android.net.Uri import android.platform.test.annotations.EnableFlags import android.text.SpannableString import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.settings.FakeUserTracker import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest Loading @@ -48,9 +53,21 @@ class ActionIntentCreatorTest : SysuiTestCase() { private val mainDispatcher = UnconfinedTestDispatcher(scheduler) private val testScope = TestScope(mainDispatcher) val packageManager = mock<PackageManager>() val userTracker = FakeUserTracker() val creator = ActionIntentCreator(context, packageManager, testScope.backgroundScope, mainDispatcher) ActionIntentCreator( context, packageManager, userTracker, testScope.backgroundScope, mainDispatcher, ) @Before fun setup() { userTracker.set(listOf(UserInfo(17, "test user", 0)), 0) } @Test fun test_getTextEditorIntent() { Loading Loading @@ -81,11 +98,22 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals(fakeComponent, intent.component) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_remoteCopyIntent_containsUserId() { context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "") val clipData = ClipData.newPlainText("Test", "Test Item") var intent = creator.getRemoteCopyIntent(clipData, context) assertEquals(17, intent.contentUserHint) } @Test fun test_getImageEditIntent_noDefault() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") var intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(Intent.ACTION_EDIT, intent.action) assertEquals("image/*", intent.type) Loading @@ -94,6 +122,16 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertFlags(intent, EXTERNAL_INTENT_FLAGS) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getImageEditIntent_containsUserId() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") val intent = creator.getImageEditIntent(fakeUri) assertEquals(17, intent.contentUserHint) } @Test fun test_getImageEditIntent_defaultProvided() = runTest { val fakeUri = Uri.parse("content://foo") Loading @@ -103,11 +141,12 @@ class ActionIntentCreatorTest : SysuiTestCase() { context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(fakeComponent, intent.component) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getImageEditIntent_preferredProvidedButDisabled() = runTest { val fakeUri = Uri.parse("content://foo") Loading @@ -130,8 +169,9 @@ class ActionIntentCreatorTest : SysuiTestCase() { R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(defaultComponent, intent.component) assertEquals(17, intent.contentUserHint) } @Test Loading Loading @@ -163,7 +203,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(preferredComponent, intent.component) } Loading @@ -179,10 +219,19 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals("text/plain", target?.type) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getShareIntent_containsUserId() { val clipData = ClipData.newPlainText("Test", "Test Item") val intent = creator.getShareIntent(clipData, context) assertEquals(17, intent.contentUserHint) } @Test fun test_getShareIntent_html() { val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>") val intent = creator.getShareIntent(clipData, getContext()) val intent = creator.getShareIntent(clipData, context) assertEquals(Intent.ACTION_CHOOSER, intent.action) assertFlags(intent, EXTERNAL_INTENT_FLAGS) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java +50 −7 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.clipboardoverlay; import static com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.ClipData; import android.content.ComponentName; import android.content.Intent; import android.content.pm.UserInfo; import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.text.SpannableString; import androidx.test.filters.SmallTest; Loading @@ -30,10 +34,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; import com.android.systemui.settings.FakeUserTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @SmallTest Loading @@ -41,8 +48,14 @@ import java.util.concurrent.atomic.AtomicReference; 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 FakeUserTracker mFakeUserTracker = new FakeUserTracker(); private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator(mFakeUserTracker); private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator(); @Before public void setup() { mFakeUserTracker.set(List.of(new UserInfo(17, "test user", 0)), 0); } @Test public void test_getTextEditorIntent() { Loading Loading @@ -74,15 +87,25 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { assertEquals(fakeComponent, intent.getComponent()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getRemoteCopyIntent_setsUserId() { getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, ""); ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext()); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getImageEditIntentAsync() { getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, ""); Uri fakeUri = Uri.parse("content://foo"); final AtomicReference<Intent> intentHolder = new AtomicReference<>(null); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> { intentHolder.set(output); }); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); Intent intent = intentHolder.get(); assertEquals(Intent.ACTION_EDIT, intent.getAction()); Loading @@ -96,12 +119,23 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { "com.android.remotecopy.RemoteCopyActivity"); getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> { intentHolder.set(output); }); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); assertEquals(fakeComponent, intentHolder.get().getComponent()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getImageEditAsync_setsUserId() { getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, ""); Uri fakeUri = Uri.parse("content://foo"); final AtomicReference<Intent> intentHolder = new AtomicReference<>(null); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); Intent intent = intentHolder.get(); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getShareIntent_plaintext() { ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Loading @@ -114,6 +148,15 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { assertEquals("text/plain", target.getType()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getShareIntent_setsUserId() { ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getShareIntent_html() { ClipData clipData = ClipData.newHtmlText("Test", "Some HTML", Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt +19 −5 Original line number Diff line number Diff line Loading @@ -25,10 +25,12 @@ import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException import android.net.Uri import android.text.TextUtils import com.android.systemui.Flags.clipboardOverlayMultiuser import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher Loading @@ -42,6 +44,7 @@ class ActionIntentCreator constructor( private val context: Context, private val packageManager: PackageManager, private val userTracker: UserTracker, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : IntentCreator { Loading Loading @@ -76,12 +79,17 @@ constructor( } } return Intent.createChooser(shareIntent, null) val chooserIntent = Intent.createChooser(shareIntent, null) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) if (clipboardOverlayMultiuser()) { chooserIntent.prepareToLeaveUser(userTracker.userId) } return chooserIntent } suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent { suspend fun getImageEditIntent(uri: Uri?): Intent { return Intent(Intent.ACTION_EDIT).apply { // Use the preferred editor if it's available, otherwise fall back to the default editor component = preferredEditor() ?: defaultEditor() Loading @@ -89,6 +97,9 @@ constructor( 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) if (clipboardOverlayMultiuser()) { prepareToLeaveUser(userTracker.userId) } } } Loading @@ -97,7 +108,7 @@ constructor( context: Context, outputConsumer: Consumer<Intent>, ) { applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) } applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri)) } } override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent { Loading @@ -110,6 +121,9 @@ constructor( setClipData(clipData) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) if (clipboardOverlayMultiuser()) { prepareToLeaveUser(userTracker.userId) } } } Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +26 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS; import static com.android.systemui.Flags.clipboardAnnounceLiveRegion; import static com.android.systemui.Flags.clipboardOverlayMultiuser; import static com.android.systemui.Flags.showClipboardIndication; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED; Loading Loading @@ -58,8 +59,11 @@ 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.plugins.ActivityStartOptions; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; import com.android.systemui.screenshot.TimeoutHandler; import com.android.systemui.settings.UserTracker; import kotlin.Unit; Loading Loading @@ -92,6 +96,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final ClipboardImageLoader mClipboardImageLoader; private final ClipboardTransitionExecutor mTransitionExecutor; private final ClipboardInputEventReceiver mClipboardInputEventReceiver; private final ActivityStarter mActivityStarter; private final UserTracker mUserTracker; private final ClipboardOverlayView mView; Loading Loading @@ -125,6 +131,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv BroadcastDispatcher broadcastDispatcher, BroadcastSender broadcastSender, TimeoutHandler timeoutHandler, ActivityStarter activityStarter, UserTracker userTracker, ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, ClipboardImageLoader clipboardImageLoader, Loading @@ -139,6 +147,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mTransitionExecutor = transitionExecutor; mClipboardInputEventReceiver = clipboardInputEventReceiver; mClipboardIndicationProvider = clipboardIndicationProvider; mActivityStarter = activityStarter; mUserTracker = userTracker; mClipboardLogger = new ClipboardLogger(uiEventLogger); mIntentCreator = intentCreator; Loading Loading @@ -475,8 +485,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv if (!mCancelled) { mClipboardLogger.logSessionComplete(event); if (intent != null) { if (clipboardOverlayMultiuser()) { mActivityStarter.startActivityDismissingKeyguard( new ActivityStartOptions(intent, false, false, null, intent.getFlags(), null, null, false, mUserTracker.getUserHandle(), null)); } else { mContext.startActivity(intent); } } hideImmediate(); } } Loading Loading @@ -529,13 +546,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv @Override public void onRemoteCopyButtonTapped() { finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED, mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); mIntentCreator.getRemoteCopyIntent( mClipboardModel.getClipData(), mContext)); } @Override public void onShareButtonTapped() { Intent shareIntent = mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext); mIntentCreator.getShareIntent( mClipboardModel.getClipData(), mContext); switch (mClipboardModel.getType()) { case TEXT: case URI: Loading @@ -555,10 +574,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mIntentCreator.getTextEditorIntent(mContext)); break; case IMAGE: mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext, intent -> { finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent); }); mIntentCreator.getImageEditIntentAsync( mClipboardModel.getUri(), mContext, intent -> finishWithSharedTransition( CLIPBOARD_OVERLAY_EDIT_TAPPED, intent)); break; default: Log.w(TAG, "Got preview tapped callback for non-editable type " Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt +24 −4 Original line number Diff line number Diff line Loading @@ -31,12 +31,21 @@ import android.view.View import android.view.Window import android.view.WindowManagerGlobal import com.android.internal.app.ChooserActivity import com.android.systemui.Flags.clipboardOverlayMultiuser import com.android.systemui.plugins.ActivityStartOptions import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.DisplayTracker import com.android.systemui.settings.UserTracker import javax.inject.Inject class ClipboardTransitionExecutor @Inject constructor(val context: Context, val displayTracker: DisplayTracker) { constructor( val context: Context, val userTracker: UserTracker, val displayTracker: DisplayTracker, val activityStarter: ActivityStarter, ) { fun startSharedTransition(window: Window, view: View, intent: Intent, onReady: Runnable) { val transition: Pair<ActivityOptions, ExitTransitionCoordinator> = ActivityOptions.startSharedElementAnimation( Loading @@ -53,10 +62,21 @@ constructor(val context: Context, val displayTracker: DisplayTracker) { override fun onFinish() {} }, null, Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME), ) transition.second.startExit() if (clipboardOverlayMultiuser()) { activityStarter.startActivityDismissingKeyguard( ActivityStartOptions( intent, flags = intent.flags, userHandle = userTracker.userHandle, activityOptions = transition.first, ) ) } else { context.startActivity(intent, transition.first.toBundle()) } val runner = RemoteAnimationAdapter(NULL_ACTIVITY_TRANSITION, 0, 0) try { checkNotNull(WindowManagerGlobal.getWindowManagerService()) Loading @@ -79,7 +99,7 @@ constructor(val context: Context, val displayTracker: DisplayTracker) { apps: Array<RemoteAnimationTarget>, wallpapers: Array<RemoteAnimationTarget>, nonApps: Array<RemoteAnimationTarget>, finishedCallback: IRemoteAnimationFinishedCallback finishedCallback: IRemoteAnimationFinishedCallback, ) { try { finishedCallback.onAnimationFinished() Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt +57 −8 Original line number Diff line number Diff line Loading @@ -22,23 +22,28 @@ import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.UserInfo import android.net.Uri import android.platform.test.annotations.EnableFlags import android.text.SpannableString import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.settings.FakeUserTracker import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest Loading @@ -48,9 +53,21 @@ class ActionIntentCreatorTest : SysuiTestCase() { private val mainDispatcher = UnconfinedTestDispatcher(scheduler) private val testScope = TestScope(mainDispatcher) val packageManager = mock<PackageManager>() val userTracker = FakeUserTracker() val creator = ActionIntentCreator(context, packageManager, testScope.backgroundScope, mainDispatcher) ActionIntentCreator( context, packageManager, userTracker, testScope.backgroundScope, mainDispatcher, ) @Before fun setup() { userTracker.set(listOf(UserInfo(17, "test user", 0)), 0) } @Test fun test_getTextEditorIntent() { Loading Loading @@ -81,11 +98,22 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals(fakeComponent, intent.component) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_remoteCopyIntent_containsUserId() { context.getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, "") val clipData = ClipData.newPlainText("Test", "Test Item") var intent = creator.getRemoteCopyIntent(clipData, context) assertEquals(17, intent.contentUserHint) } @Test fun test_getImageEditIntent_noDefault() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") var intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(Intent.ACTION_EDIT, intent.action) assertEquals("image/*", intent.type) Loading @@ -94,6 +122,16 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertFlags(intent, EXTERNAL_INTENT_FLAGS) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getImageEditIntent_containsUserId() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") val intent = creator.getImageEditIntent(fakeUri) assertEquals(17, intent.contentUserHint) } @Test fun test_getImageEditIntent_defaultProvided() = runTest { val fakeUri = Uri.parse("content://foo") Loading @@ -103,11 +141,12 @@ class ActionIntentCreatorTest : SysuiTestCase() { context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(fakeComponent, intent.component) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getImageEditIntent_preferredProvidedButDisabled() = runTest { val fakeUri = Uri.parse("content://foo") Loading @@ -130,8 +169,9 @@ class ActionIntentCreatorTest : SysuiTestCase() { R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(defaultComponent, intent.component) assertEquals(17, intent.contentUserHint) } @Test Loading Loading @@ -163,7 +203,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri) assertEquals(preferredComponent, intent.component) } Loading @@ -179,10 +219,19 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals("text/plain", target?.type) } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) fun test_getShareIntent_containsUserId() { val clipData = ClipData.newPlainText("Test", "Test Item") val intent = creator.getShareIntent(clipData, context) assertEquals(17, intent.contentUserHint) } @Test fun test_getShareIntent_html() { val clipData = ClipData.newHtmlText("Test", "Some HTML", "<b>Some HTML</b>") val intent = creator.getShareIntent(clipData, getContext()) val intent = creator.getShareIntent(clipData, context) assertEquals(Intent.ACTION_CHOOSER, intent.action) assertFlags(intent, EXTERNAL_INTENT_FLAGS) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/DefaultIntentCreatorTest.java +50 −7 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.clipboardoverlay; import static com.android.systemui.Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.ClipData; import android.content.ComponentName; import android.content.Intent; import android.content.pm.UserInfo; import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.text.SpannableString; import androidx.test.filters.SmallTest; Loading @@ -30,10 +34,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; import com.android.systemui.settings.FakeUserTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @SmallTest Loading @@ -41,8 +48,14 @@ import java.util.concurrent.atomic.AtomicReference; 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 FakeUserTracker mFakeUserTracker = new FakeUserTracker(); private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator(mFakeUserTracker); private final DefaultIntentCreator mIntentCreator = new DefaultIntentCreator(); @Before public void setup() { mFakeUserTracker.set(List.of(new UserInfo(17, "test user", 0)), 0); } @Test public void test_getTextEditorIntent() { Loading Loading @@ -74,15 +87,25 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { assertEquals(fakeComponent, intent.getComponent()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getRemoteCopyIntent_setsUserId() { getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage, ""); ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Intent intent = mIntentCreator.getRemoteCopyIntent(clipData, getContext()); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getImageEditIntentAsync() { getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, ""); Uri fakeUri = Uri.parse("content://foo"); final AtomicReference<Intent> intentHolder = new AtomicReference<>(null); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> { intentHolder.set(output); }); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); Intent intent = intentHolder.get(); assertEquals(Intent.ACTION_EDIT, intent.getAction()); Loading @@ -96,12 +119,23 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { "com.android.remotecopy.RemoteCopyActivity"); getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), output -> { intentHolder.set(output); }); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); assertEquals(fakeComponent, intentHolder.get().getComponent()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getImageEditAsync_setsUserId() { getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, ""); Uri fakeUri = Uri.parse("content://foo"); final AtomicReference<Intent> intentHolder = new AtomicReference<>(null); mIntentCreator.getImageEditIntentAsync(fakeUri, getContext(), intentHolder::set); Intent intent = intentHolder.get(); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getShareIntent_plaintext() { ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Loading @@ -114,6 +148,15 @@ public class DefaultIntentCreatorTest extends SysuiTestCase { assertEquals("text/plain", target.getType()); } @Test @EnableFlags(FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_getShareIntent_setsUserId() { ClipData clipData = ClipData.newPlainText("Test", "Test Item"); Intent intent = mIntentCreator.getShareIntent(clipData, getContext()); assertEquals(17, intent.getContentUserHint()); } @Test public void test_getShareIntent_html() { ClipData clipData = ClipData.newHtmlText("Test", "Some HTML", Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt +19 −5 Original line number Diff line number Diff line Loading @@ -25,10 +25,12 @@ import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException import android.net.Uri import android.text.TextUtils import com.android.systemui.Flags.clipboardOverlayMultiuser import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher Loading @@ -42,6 +44,7 @@ class ActionIntentCreator constructor( private val context: Context, private val packageManager: PackageManager, private val userTracker: UserTracker, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : IntentCreator { Loading Loading @@ -76,12 +79,17 @@ constructor( } } return Intent.createChooser(shareIntent, null) val chooserIntent = Intent.createChooser(shareIntent, null) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) if (clipboardOverlayMultiuser()) { chooserIntent.prepareToLeaveUser(userTracker.userId) } return chooserIntent } suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent { suspend fun getImageEditIntent(uri: Uri?): Intent { return Intent(Intent.ACTION_EDIT).apply { // Use the preferred editor if it's available, otherwise fall back to the default editor component = preferredEditor() ?: defaultEditor() Loading @@ -89,6 +97,9 @@ constructor( 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) if (clipboardOverlayMultiuser()) { prepareToLeaveUser(userTracker.userId) } } } Loading @@ -97,7 +108,7 @@ constructor( context: Context, outputConsumer: Consumer<Intent>, ) { applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri, context)) } applicationScope.launch { outputConsumer.accept(getImageEditIntent(uri)) } } override fun getRemoteCopyIntent(clipData: ClipData?, context: Context): Intent { Loading @@ -110,6 +121,9 @@ constructor( setClipData(clipData) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) if (clipboardOverlayMultiuser()) { prepareToLeaveUser(userTracker.userId) } } } Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +26 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS; import static com.android.systemui.Flags.clipboardAnnounceLiveRegion; import static com.android.systemui.Flags.clipboardOverlayMultiuser; import static com.android.systemui.Flags.showClipboardIndication; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED; Loading Loading @@ -58,8 +59,11 @@ 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.plugins.ActivityStartOptions; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; import com.android.systemui.screenshot.TimeoutHandler; import com.android.systemui.settings.UserTracker; import kotlin.Unit; Loading Loading @@ -92,6 +96,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final ClipboardImageLoader mClipboardImageLoader; private final ClipboardTransitionExecutor mTransitionExecutor; private final ClipboardInputEventReceiver mClipboardInputEventReceiver; private final ActivityStarter mActivityStarter; private final UserTracker mUserTracker; private final ClipboardOverlayView mView; Loading Loading @@ -125,6 +131,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv BroadcastDispatcher broadcastDispatcher, BroadcastSender broadcastSender, TimeoutHandler timeoutHandler, ActivityStarter activityStarter, UserTracker userTracker, ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, ClipboardImageLoader clipboardImageLoader, Loading @@ -139,6 +147,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mTransitionExecutor = transitionExecutor; mClipboardInputEventReceiver = clipboardInputEventReceiver; mClipboardIndicationProvider = clipboardIndicationProvider; mActivityStarter = activityStarter; mUserTracker = userTracker; mClipboardLogger = new ClipboardLogger(uiEventLogger); mIntentCreator = intentCreator; Loading Loading @@ -475,8 +485,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv if (!mCancelled) { mClipboardLogger.logSessionComplete(event); if (intent != null) { if (clipboardOverlayMultiuser()) { mActivityStarter.startActivityDismissingKeyguard( new ActivityStartOptions(intent, false, false, null, intent.getFlags(), null, null, false, mUserTracker.getUserHandle(), null)); } else { mContext.startActivity(intent); } } hideImmediate(); } } Loading Loading @@ -529,13 +546,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv @Override public void onRemoteCopyButtonTapped() { finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED, mIntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); mIntentCreator.getRemoteCopyIntent( mClipboardModel.getClipData(), mContext)); } @Override public void onShareButtonTapped() { Intent shareIntent = mIntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext); mIntentCreator.getShareIntent( mClipboardModel.getClipData(), mContext); switch (mClipboardModel.getType()) { case TEXT: case URI: Loading @@ -555,10 +574,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mIntentCreator.getTextEditorIntent(mContext)); break; case IMAGE: mIntentCreator.getImageEditIntentAsync(mClipboardModel.getUri(), mContext, intent -> { finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, intent); }); mIntentCreator.getImageEditIntentAsync( mClipboardModel.getUri(), mContext, intent -> finishWithSharedTransition( CLIPBOARD_OVERLAY_EDIT_TAPPED, intent)); break; default: Log.w(TAG, "Got preview tapped callback for non-editable type " Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt +24 −4 Original line number Diff line number Diff line Loading @@ -31,12 +31,21 @@ import android.view.View import android.view.Window import android.view.WindowManagerGlobal import com.android.internal.app.ChooserActivity import com.android.systemui.Flags.clipboardOverlayMultiuser import com.android.systemui.plugins.ActivityStartOptions import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.DisplayTracker import com.android.systemui.settings.UserTracker import javax.inject.Inject class ClipboardTransitionExecutor @Inject constructor(val context: Context, val displayTracker: DisplayTracker) { constructor( val context: Context, val userTracker: UserTracker, val displayTracker: DisplayTracker, val activityStarter: ActivityStarter, ) { fun startSharedTransition(window: Window, view: View, intent: Intent, onReady: Runnable) { val transition: Pair<ActivityOptions, ExitTransitionCoordinator> = ActivityOptions.startSharedElementAnimation( Loading @@ -53,10 +62,21 @@ constructor(val context: Context, val displayTracker: DisplayTracker) { override fun onFinish() {} }, null, Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME), ) transition.second.startExit() if (clipboardOverlayMultiuser()) { activityStarter.startActivityDismissingKeyguard( ActivityStartOptions( intent, flags = intent.flags, userHandle = userTracker.userHandle, activityOptions = transition.first, ) ) } else { context.startActivity(intent, transition.first.toBundle()) } val runner = RemoteAnimationAdapter(NULL_ACTIVITY_TRANSITION, 0, 0) try { checkNotNull(WindowManagerGlobal.getWindowManagerService()) Loading @@ -79,7 +99,7 @@ constructor(val context: Context, val displayTracker: DisplayTracker) { apps: Array<RemoteAnimationTarget>, wallpapers: Array<RemoteAnimationTarget>, nonApps: Array<RemoteAnimationTarget>, finishedCallback: IRemoteAnimationFinishedCallback finishedCallback: IRemoteAnimationFinishedCallback, ) { try { finishedCallback.onAnimationFinished() Loading