Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 870417e1 authored by Miranda Kephart's avatar Miranda Kephart Committed by Android (Google) Code Review
Browse files

Merge changes I7d1c8edb,Ia9aa6f1a into main

* changes:
  Use user context for the clipboard overlay window
  Use UserTracker to get the correct clipboard manager
parents c2546fe4 ff6ca997
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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"
+46 −10
Original line number Diff line number Diff line
@@ -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) {
+17 −5
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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);
        }
    }

    /**
+6 −0
Original line number Diff line number Diff line
@@ -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) {
+112 −15
Original line number Diff line number Diff line
@@ -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() {
@@ -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() {