Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -681,6 +681,16 @@ flag { bug: "368308908" } flag { name: "clipboard_overlay_multiuser" namespace: "systemui" description: "Fix clipboard overlay for secondary users" bug: "217922018" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "clipboard_shared_transitions" namespace: "systemui" Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java +46 −10 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.clipboardoverlay; import static android.content.ClipDescription.CLASSIFICATION_COMPLETE; import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen; import static com.android.systemui.Flags.clipboardOverlayMultiuser; import static com.android.systemui.Flags.overrideSuppressOverlayCondition; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED; Loading @@ -35,12 +36,18 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; import com.android.systemui.user.utils.UserScopedService; import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Provider; Loading @@ -61,42 +68,71 @@ public class ClipboardListener implements private final Context mContext; private final Provider<ClipboardOverlayController> mOverlayProvider; private final ClipboardToast mClipboardToast; private final ClipboardManager mClipboardManager; private final KeyguardManager mKeyguardManager; private final UserScopedService<ClipboardManager> mClipboardManagerProvider; private final UserScopedService<KeyguardManager> mKeyguardManagerProvider; private final UiEventLogger mUiEventLogger; private final ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController; private ClipboardOverlay mClipboardOverlay; private ClipboardManager mClipboardManagerForUser; private KeyguardManager mKeyguardManagerForUser; private final UserTracker mUserTracker; private final Executor mMainExecutor; private final UserTracker.Callback mCallback = new UserTracker.Callback() { @Override public void onUserChanged(int newUser, @NonNull Context userContext) { UserTracker.Callback.super.onUserChanged(newUser, userContext); mClipboardManagerForUser.removePrimaryClipChangedListener(ClipboardListener.this); setUser(mUserTracker.getUserHandle()); mClipboardManagerForUser.addPrimaryClipChangedListener(ClipboardListener.this); } }; @Inject public ClipboardListener(Context context, Provider<ClipboardOverlayController> clipboardOverlayControllerProvider, ClipboardToast clipboardToast, UserTracker userTracker, UserScopedService<ClipboardManager> clipboardManager, KeyguardManager keyguardManager, UserScopedService<KeyguardManager> keyguardManager, UiEventLogger uiEventLogger, @Main Executor mainExecutor, ClipboardOverlaySuppressionController clipboardOverlaySuppressionController) { mContext = context; mOverlayProvider = clipboardOverlayControllerProvider; mClipboardToast = clipboardToast; mClipboardManager = clipboardManager.forUser(UserHandle.CURRENT); mKeyguardManager = keyguardManager; mClipboardManagerProvider = clipboardManager; mKeyguardManagerProvider = keyguardManager; mUiEventLogger = uiEventLogger; mClipboardOverlaySuppressionController = clipboardOverlaySuppressionController; mMainExecutor = mainExecutor; mUserTracker = userTracker; setUser(mUserTracker.getUserHandle()); } private void setUser(UserHandle user) { mClipboardManagerForUser = mClipboardManagerProvider.forUser(user); mKeyguardManagerForUser = mKeyguardManagerProvider.forUser(user); } @Override public void start() { mClipboardManager.addPrimaryClipChangedListener(this); if (clipboardOverlayMultiuser()) { mUserTracker.addCallback(mCallback, mMainExecutor); } mClipboardManagerForUser.addPrimaryClipChangedListener(this); } @Override public void onPrimaryClipChanged() { if (!mClipboardManager.hasPrimaryClip()) { if (!mClipboardManagerForUser.hasPrimaryClip()) { return; } String clipSource = mClipboardManager.getPrimaryClipSource(); ClipData clipData = mClipboardManager.getPrimaryClip(); String clipSource = mClipboardManagerForUser.getPrimaryClipSource(); ClipData clipData = mClipboardManagerForUser.getPrimaryClip(); if (overrideSuppressOverlayCondition()) { if (mClipboardOverlaySuppressionController.shouldSuppressOverlay(clipData, clipSource, Loading @@ -112,7 +148,7 @@ public class ClipboardListener implements } // user should not access intents before setup or while device is locked if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManager.isDeviceLocked()) if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManagerForUser.isDeviceLocked()) || !isUserSetupComplete() || clipData == null // shouldn't happen, but just in case || clipData.getItemCount() == 0) { Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java +17 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.clipboardoverlay.dagger; 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.util.ConvenienceExtensionsKt.toKotlinLazy; Loading @@ -34,6 +35,7 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.clipboardoverlay.ClipboardOverlayView; import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import dagger.Lazy; import dagger.Module; Loading @@ -54,18 +56,28 @@ public interface ClipboardOverlayModule { @Provides @OverlayWindowContext static Context provideWindowContext(DisplayManager displayManager, DisplayTracker displayTracker, Context context) { DisplayTracker displayTracker, Context context, UserTracker userTracker) { Display display = displayManager.getDisplay(displayTracker.getDefaultDisplayId()); if (clipboardOverlayMultiuser()) { return userTracker.getUserContext().createWindowContext(display, TYPE_SCREENSHOT, null); } else { return context.createWindowContext(display, TYPE_SCREENSHOT, null); } } /** * */ @Provides static ClipboardOverlayView provideClipboardOverlayView(@OverlayWindowContext Context context) { static ClipboardOverlayView provideClipboardOverlayView( @OverlayWindowContext Context overlayContext, Context context) { if (clipboardOverlayMultiuser()) { return (ClipboardOverlayView) LayoutInflater.from(context).inflate( R.layout.clipboard_overlay, null); } else { return (ClipboardOverlayView) LayoutInflater.from(overlayContext).inflate( R.layout.clipboard_overlay, null); } } /** Loading packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -428,6 +428,12 @@ public class FrameworkServicesModule { return context.getSystemService(KeyguardManager.class); } @Provides @Singleton static UserScopedService<KeyguardManager> provideKeyguardManagerUserScoped(Context context) { return new UserScopedServiceImpl<>(context, KeyguardManager.class); } @Provides @Singleton static LatencyTracker provideLatencyTracker(Context context) { Loading packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java +112 −15 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.KeyguardManager; import android.content.ClipData; import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.pm.UserInfo; import android.os.Build; import android.os.PersistableBundle; import android.os.UserHandle; Loading @@ -45,6 +46,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.UiEventLogger; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.settings.FakeUserTracker; import com.android.systemui.user.utils.FakeUserScopedService; import org.junit.Before; import org.junit.Test; Loading @@ -56,6 +59,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.ArrayList; import java.util.List; import javax.inject.Provider; Loading @@ -68,6 +72,10 @@ public class ClipboardListenerTest extends SysuiTestCase { @Mock private KeyguardManager mKeyguardManager; @Mock private ClipboardManager mClipboardManagerSecondaryUser; @Mock private KeyguardManager mKeyguardManagerSecondaryUser; @Mock private ClipboardOverlayController mOverlayController; @Mock private ClipboardToast mClipboardToast; Loading @@ -76,9 +84,6 @@ public class ClipboardListenerTest extends SysuiTestCase { @Mock private ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController; private ClipData mSampleClipData; private String mSampleSource = "Example source"; @Captor private ArgumentCaptor<Runnable> mRunnableCaptor; @Captor Loading @@ -89,6 +94,20 @@ public class ClipboardListenerTest extends SysuiTestCase { @Spy private Provider<ClipboardOverlayController> mOverlayControllerProvider; private final FakeUserScopedService<ClipboardManager> mUserScopedClipboardManager = new FakeUserScopedService<>(mClipboardManager); private final FakeUserScopedService<KeyguardManager> mUserScopedKeyguardManager = new FakeUserScopedService<>(mKeyguardManager); private final FakeUserTracker mUserTracker = new FakeUserTracker(); private final List<UserInfo> mUserInfos = List.of( new UserInfo(0, "system", 0), new UserInfo(50, "secondary", 0)); private final ClipData mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); private final ClipData mSecondaryClipData = new ClipData( "Test secondary", new String[]{"text/plain"}, new ClipData.Item("Secondary Item")); private final String mSampleSource = "Example source"; private ClipboardListener mClipboardListener; Loading @@ -97,30 +116,38 @@ public class ClipboardListenerTest extends SysuiTestCase { mOverlayControllerProvider = () -> mOverlayController; MockitoAnnotations.initMocks(this); when(mClipboardManager.hasPrimaryClip()).thenReturn(true); Settings.Secure.putInt( mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 1); mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData); when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource); mUserTracker.set(mUserInfos, 0); UserHandle user0 = mUserInfos.get(0).getUserHandle(); UserHandle user1 = mUserInfos.get(1).getUserHandle(); mUserScopedKeyguardManager.addImplementation(user0, mKeyguardManager); mUserScopedKeyguardManager.addImplementation(user1, mKeyguardManagerSecondaryUser); setupClipboardManager(mClipboardManager, user0, mSampleClipData); setupClipboardManager(mClipboardManagerSecondaryUser, user1, mSecondaryClipData); mClipboardListener = new ClipboardListener( getContext(), mOverlayControllerProvider, mClipboardToast, user -> { if (UserHandle.CURRENT.equals(user)) { return mClipboardManager; } return null; }, mKeyguardManager, mUserTracker, mUserScopedClipboardManager, mUserScopedKeyguardManager, mUiEventLogger, getContext().getMainExecutor(), mClipboardOverlaySuppressionController); } private void setupClipboardManager( ClipboardManager clipboardManager, UserHandle user, ClipData clipData) { when(clipboardManager.hasPrimaryClip()).thenReturn(true); when(clipboardManager.getPrimaryClip()).thenReturn(clipData); when(clipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource); mUserScopedClipboardManager.addImplementation(user, clipboardManager); } @Test public void test_initialization() { Loading Loading @@ -159,6 +186,76 @@ public class ClipboardListenerTest extends SysuiTestCase { verify(mOverlayControllerProvider, times(2)).get(); } @Test @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_noSwitchUserWithFlagOff() { mClipboardListener.start(); mClipboardListener.onPrimaryClipChanged(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mKeyguardManager, times(2)).isDeviceLocked(); verify(mClipboardManager, times(2)).hasPrimaryClip(); verify(mOverlayController, times(2)).setClipData(mSampleClipData, mSampleSource); verifyNoMoreInteractions(mClipboardManagerSecondaryUser); verifyNoMoreInteractions(mKeyguardManagerSecondaryUser); } @Test @EnableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_switchUserSwitchesClipboard() { mClipboardListener.start(); mClipboardListener.onPrimaryClipChanged(); verify(mClipboardManager).hasPrimaryClip(); verify(mOverlayController).setClipData(mSampleClipData, mSampleSource); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mClipboardManagerSecondaryUser).hasPrimaryClip(); verify(mOverlayController).setClipData(mSecondaryClipData, mSampleSource); } @Test @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) @EnableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN) public void test_deviceLockedForSecondaryUser_withoutMultiuser_showsOverlay() { when(mKeyguardManager.isDeviceLocked()).thenReturn(false); when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true); mClipboardListener.start(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mUiEventLogger, times(1)).log( ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource); verify(mOverlayController).setClipData(mSampleClipData, mSampleSource); verifyNoMoreInteractions(mClipboardToast); } @Test @EnableFlags({Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER, Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN}) public void test_deviceLockedForSecondaryUser_showsToast() { when(mKeyguardManager.isDeviceLocked()).thenReturn(false); when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true); mClipboardListener.start(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mUiEventLogger, times(1)).log( ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource); verify(mClipboardToast, times(1)).showCopiedToast(); verifyNoMoreInteractions(mOverlayControllerProvider); } @Test @DisableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION) public void test_shouldSuppressOverlay() { Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -681,6 +681,16 @@ flag { bug: "368308908" } flag { name: "clipboard_overlay_multiuser" namespace: "systemui" description: "Fix clipboard overlay for secondary users" bug: "217922018" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "clipboard_shared_transitions" namespace: "systemui" Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java +46 −10 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.clipboardoverlay; import static android.content.ClipDescription.CLASSIFICATION_COMPLETE; import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen; import static com.android.systemui.Flags.clipboardOverlayMultiuser; import static com.android.systemui.Flags.overrideSuppressOverlayCondition; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED; Loading @@ -35,12 +36,18 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; import com.android.systemui.user.utils.UserScopedService; import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Provider; Loading @@ -61,42 +68,71 @@ public class ClipboardListener implements private final Context mContext; private final Provider<ClipboardOverlayController> mOverlayProvider; private final ClipboardToast mClipboardToast; private final ClipboardManager mClipboardManager; private final KeyguardManager mKeyguardManager; private final UserScopedService<ClipboardManager> mClipboardManagerProvider; private final UserScopedService<KeyguardManager> mKeyguardManagerProvider; private final UiEventLogger mUiEventLogger; private final ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController; private ClipboardOverlay mClipboardOverlay; private ClipboardManager mClipboardManagerForUser; private KeyguardManager mKeyguardManagerForUser; private final UserTracker mUserTracker; private final Executor mMainExecutor; private final UserTracker.Callback mCallback = new UserTracker.Callback() { @Override public void onUserChanged(int newUser, @NonNull Context userContext) { UserTracker.Callback.super.onUserChanged(newUser, userContext); mClipboardManagerForUser.removePrimaryClipChangedListener(ClipboardListener.this); setUser(mUserTracker.getUserHandle()); mClipboardManagerForUser.addPrimaryClipChangedListener(ClipboardListener.this); } }; @Inject public ClipboardListener(Context context, Provider<ClipboardOverlayController> clipboardOverlayControllerProvider, ClipboardToast clipboardToast, UserTracker userTracker, UserScopedService<ClipboardManager> clipboardManager, KeyguardManager keyguardManager, UserScopedService<KeyguardManager> keyguardManager, UiEventLogger uiEventLogger, @Main Executor mainExecutor, ClipboardOverlaySuppressionController clipboardOverlaySuppressionController) { mContext = context; mOverlayProvider = clipboardOverlayControllerProvider; mClipboardToast = clipboardToast; mClipboardManager = clipboardManager.forUser(UserHandle.CURRENT); mKeyguardManager = keyguardManager; mClipboardManagerProvider = clipboardManager; mKeyguardManagerProvider = keyguardManager; mUiEventLogger = uiEventLogger; mClipboardOverlaySuppressionController = clipboardOverlaySuppressionController; mMainExecutor = mainExecutor; mUserTracker = userTracker; setUser(mUserTracker.getUserHandle()); } private void setUser(UserHandle user) { mClipboardManagerForUser = mClipboardManagerProvider.forUser(user); mKeyguardManagerForUser = mKeyguardManagerProvider.forUser(user); } @Override public void start() { mClipboardManager.addPrimaryClipChangedListener(this); if (clipboardOverlayMultiuser()) { mUserTracker.addCallback(mCallback, mMainExecutor); } mClipboardManagerForUser.addPrimaryClipChangedListener(this); } @Override public void onPrimaryClipChanged() { if (!mClipboardManager.hasPrimaryClip()) { if (!mClipboardManagerForUser.hasPrimaryClip()) { return; } String clipSource = mClipboardManager.getPrimaryClipSource(); ClipData clipData = mClipboardManager.getPrimaryClip(); String clipSource = mClipboardManagerForUser.getPrimaryClipSource(); ClipData clipData = mClipboardManagerForUser.getPrimaryClip(); if (overrideSuppressOverlayCondition()) { if (mClipboardOverlaySuppressionController.shouldSuppressOverlay(clipData, clipSource, Loading @@ -112,7 +148,7 @@ public class ClipboardListener implements } // user should not access intents before setup or while device is locked if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManager.isDeviceLocked()) if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManagerForUser.isDeviceLocked()) || !isUserSetupComplete() || clipData == null // shouldn't happen, but just in case || clipData.getItemCount() == 0) { Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java +17 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.clipboardoverlay.dagger; 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.util.ConvenienceExtensionsKt.toKotlinLazy; Loading @@ -34,6 +35,7 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.systemui.clipboardoverlay.ClipboardOverlayView; import com.android.systemui.res.R; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import dagger.Lazy; import dagger.Module; Loading @@ -54,18 +56,28 @@ public interface ClipboardOverlayModule { @Provides @OverlayWindowContext static Context provideWindowContext(DisplayManager displayManager, DisplayTracker displayTracker, Context context) { DisplayTracker displayTracker, Context context, UserTracker userTracker) { Display display = displayManager.getDisplay(displayTracker.getDefaultDisplayId()); if (clipboardOverlayMultiuser()) { return userTracker.getUserContext().createWindowContext(display, TYPE_SCREENSHOT, null); } else { return context.createWindowContext(display, TYPE_SCREENSHOT, null); } } /** * */ @Provides static ClipboardOverlayView provideClipboardOverlayView(@OverlayWindowContext Context context) { static ClipboardOverlayView provideClipboardOverlayView( @OverlayWindowContext Context overlayContext, Context context) { if (clipboardOverlayMultiuser()) { return (ClipboardOverlayView) LayoutInflater.from(context).inflate( R.layout.clipboard_overlay, null); } else { return (ClipboardOverlayView) LayoutInflater.from(overlayContext).inflate( R.layout.clipboard_overlay, null); } } /** Loading
packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -428,6 +428,12 @@ public class FrameworkServicesModule { return context.getSystemService(KeyguardManager.class); } @Provides @Singleton static UserScopedService<KeyguardManager> provideKeyguardManagerUserScoped(Context context) { return new UserScopedServiceImpl<>(context, KeyguardManager.class); } @Provides @Singleton static LatencyTracker provideLatencyTracker(Context context) { Loading
packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java +112 −15 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.KeyguardManager; import android.content.ClipData; import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.pm.UserInfo; import android.os.Build; import android.os.PersistableBundle; import android.os.UserHandle; Loading @@ -45,6 +46,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.logging.UiEventLogger; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.settings.FakeUserTracker; import com.android.systemui.user.utils.FakeUserScopedService; import org.junit.Before; import org.junit.Test; Loading @@ -56,6 +59,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import java.util.ArrayList; import java.util.List; import javax.inject.Provider; Loading @@ -68,6 +72,10 @@ public class ClipboardListenerTest extends SysuiTestCase { @Mock private KeyguardManager mKeyguardManager; @Mock private ClipboardManager mClipboardManagerSecondaryUser; @Mock private KeyguardManager mKeyguardManagerSecondaryUser; @Mock private ClipboardOverlayController mOverlayController; @Mock private ClipboardToast mClipboardToast; Loading @@ -76,9 +84,6 @@ public class ClipboardListenerTest extends SysuiTestCase { @Mock private ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController; private ClipData mSampleClipData; private String mSampleSource = "Example source"; @Captor private ArgumentCaptor<Runnable> mRunnableCaptor; @Captor Loading @@ -89,6 +94,20 @@ public class ClipboardListenerTest extends SysuiTestCase { @Spy private Provider<ClipboardOverlayController> mOverlayControllerProvider; private final FakeUserScopedService<ClipboardManager> mUserScopedClipboardManager = new FakeUserScopedService<>(mClipboardManager); private final FakeUserScopedService<KeyguardManager> mUserScopedKeyguardManager = new FakeUserScopedService<>(mKeyguardManager); private final FakeUserTracker mUserTracker = new FakeUserTracker(); private final List<UserInfo> mUserInfos = List.of( new UserInfo(0, "system", 0), new UserInfo(50, "secondary", 0)); private final ClipData mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); private final ClipData mSecondaryClipData = new ClipData( "Test secondary", new String[]{"text/plain"}, new ClipData.Item("Secondary Item")); private final String mSampleSource = "Example source"; private ClipboardListener mClipboardListener; Loading @@ -97,30 +116,38 @@ public class ClipboardListenerTest extends SysuiTestCase { mOverlayControllerProvider = () -> mOverlayController; MockitoAnnotations.initMocks(this); when(mClipboardManager.hasPrimaryClip()).thenReturn(true); Settings.Secure.putInt( mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 1); mSampleClipData = new ClipData("Test", new String[]{"text/plain"}, new ClipData.Item("Test Item")); when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData); when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource); mUserTracker.set(mUserInfos, 0); UserHandle user0 = mUserInfos.get(0).getUserHandle(); UserHandle user1 = mUserInfos.get(1).getUserHandle(); mUserScopedKeyguardManager.addImplementation(user0, mKeyguardManager); mUserScopedKeyguardManager.addImplementation(user1, mKeyguardManagerSecondaryUser); setupClipboardManager(mClipboardManager, user0, mSampleClipData); setupClipboardManager(mClipboardManagerSecondaryUser, user1, mSecondaryClipData); mClipboardListener = new ClipboardListener( getContext(), mOverlayControllerProvider, mClipboardToast, user -> { if (UserHandle.CURRENT.equals(user)) { return mClipboardManager; } return null; }, mKeyguardManager, mUserTracker, mUserScopedClipboardManager, mUserScopedKeyguardManager, mUiEventLogger, getContext().getMainExecutor(), mClipboardOverlaySuppressionController); } private void setupClipboardManager( ClipboardManager clipboardManager, UserHandle user, ClipData clipData) { when(clipboardManager.hasPrimaryClip()).thenReturn(true); when(clipboardManager.getPrimaryClip()).thenReturn(clipData); when(clipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource); mUserScopedClipboardManager.addImplementation(user, clipboardManager); } @Test public void test_initialization() { Loading Loading @@ -159,6 +186,76 @@ public class ClipboardListenerTest extends SysuiTestCase { verify(mOverlayControllerProvider, times(2)).get(); } @Test @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_noSwitchUserWithFlagOff() { mClipboardListener.start(); mClipboardListener.onPrimaryClipChanged(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mKeyguardManager, times(2)).isDeviceLocked(); verify(mClipboardManager, times(2)).hasPrimaryClip(); verify(mOverlayController, times(2)).setClipData(mSampleClipData, mSampleSource); verifyNoMoreInteractions(mClipboardManagerSecondaryUser); verifyNoMoreInteractions(mKeyguardManagerSecondaryUser); } @Test @EnableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) public void test_switchUserSwitchesClipboard() { mClipboardListener.start(); mClipboardListener.onPrimaryClipChanged(); verify(mClipboardManager).hasPrimaryClip(); verify(mOverlayController).setClipData(mSampleClipData, mSampleSource); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mClipboardManagerSecondaryUser).hasPrimaryClip(); verify(mOverlayController).setClipData(mSecondaryClipData, mSampleSource); } @Test @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER) @EnableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN) public void test_deviceLockedForSecondaryUser_withoutMultiuser_showsOverlay() { when(mKeyguardManager.isDeviceLocked()).thenReturn(false); when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true); mClipboardListener.start(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mUiEventLogger, times(1)).log( ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource); verify(mOverlayController).setClipData(mSampleClipData, mSampleSource); verifyNoMoreInteractions(mClipboardToast); } @Test @EnableFlags({Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER, Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN}) public void test_deviceLockedForSecondaryUser_showsToast() { when(mKeyguardManager.isDeviceLocked()).thenReturn(false); when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true); mClipboardListener.start(); mUserTracker.set(mUserInfos, 1); mUserTracker.onUserChanged(mUserInfos.get(1).id); mClipboardListener.onPrimaryClipChanged(); verify(mUiEventLogger, times(1)).log( ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource); verify(mClipboardToast, times(1)).showCopiedToast(); verifyNoMoreInteractions(mOverlayControllerProvider); } @Test @DisableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION) public void test_shouldSuppressOverlay() { Loading