summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Miranda Kephart <mkephart@google.com> 2024-12-05 13:05:39 -0500
committer Miranda Kephart <mkephart@google.com> 2024-12-16 09:53:23 -0500
commit6860d18582895f16ee46ed205668045d78778bc2 (patch)
treefb1e058ea64f73ad817945ae1b3dae2e1243799a
parentd322e4e79b63d6a456ea9f6aa5921b9637194596 (diff)
Use UserTracker to get the correct clipboard manager
Currently we only start the clipboard manager once, so it doesn't work after switching to a secondary user. This change listens for user switches and updates the clipboard and keyguard managers to be associated with the current user. Note that there is still an issue causing the clipboard overlay window to not display for secondary users. This is a preliminary change that just fixes the issue of listening to the correct user. Bug: 217922018 Test: manual via adding a secondary user, verified via logs Flag: com.android.systemui.clipboard_overlay_multiuser Change-Id: Ia9aa6f1a79d8f964c0b3831f901b6c66edeea3d0
-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