summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java127
4 files changed, 174 insertions, 25 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a4b8821383e0..ff42ba378dc4 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -679,6 +679,16 @@ flag {
}
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"
description: "Show shared transitions from clipboard"
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 7033e641dc2c..c0c4ec335a34 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -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;
@@ -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;
@@ -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,
@@ -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) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 78a8a42e2743..a15065736e45 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -429,6 +429,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static UserScopedService<KeyguardManager> provideKeyguardManagerUserScoped(Context context) {
+ return new UserScopedServiceImpl<>(context, KeyguardManager.class);
+ }
+
+ @Provides
+ @Singleton
static LatencyTracker provideLatencyTracker(Context context) {
return LatencyTracker.getInstance(context);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index d8d53e006ab6..0c7989df7293 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -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;
@@ -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;
@@ -56,6 +59,7 @@ import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.ArrayList;
+import java.util.List;
import javax.inject.Provider;
@@ -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;
@@ -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
@@ -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;
@@ -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() {
@@ -160,6 +187,76 @@ public class ClipboardListenerTest extends SysuiTestCase {
}
@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() {
// Regardless of the package or emulator, nothing should be suppressed without the flag