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

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

Merge "Persist copy UI from remote devices" into tm-qpr-dev

parents 4046d6b3 6da7d865
Loading
Loading
Loading
Loading
+29 −5
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;


import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNull;


@@ -73,6 +74,7 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
import com.android.systemui.screenshot.TimeoutHandler;


import java.io.IOException;
import java.io.IOException;
@@ -101,6 +103,8 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
    private final ClipboardOverlayWindow mWindow;
    private final ClipboardOverlayWindow mWindow;
    private final TimeoutHandler mTimeoutHandler;
    private final TimeoutHandler mTimeoutHandler;
    private final TextClassifier mTextClassifier;
    private final TextClassifier mTextClassifier;
    private final ClipboardOverlayUtils mClipboardUtils;
    private final FeatureFlags mFeatureFlags;


    private final ClipboardOverlayView mView;
    private final ClipboardOverlayView mView;


@@ -119,11 +123,15 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
    private Animator mExitAnimator;
    private Animator mExitAnimator;
    private Animator mEnterAnimator;
    private Animator mEnterAnimator;


    private Runnable mOnUiUpdate;

    private final ClipboardOverlayView.ClipboardOverlayCallbacks mClipboardCallbacks =
    private final ClipboardOverlayView.ClipboardOverlayCallbacks mClipboardCallbacks =
            new ClipboardOverlayView.ClipboardOverlayCallbacks() {
            new ClipboardOverlayView.ClipboardOverlayCallbacks() {
                @Override
                @Override
                public void onInteraction() {
                public void onInteraction() {
                    mTimeoutHandler.resetTimeout();
                    if (mOnUiUpdate != null) {
                        mOnUiUpdate.run();
                    }
                }
                }


                @Override
                @Override
@@ -178,7 +186,10 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
            ClipboardOverlayWindow clipboardOverlayWindow,
            ClipboardOverlayWindow clipboardOverlayWindow,
            BroadcastDispatcher broadcastDispatcher,
            BroadcastDispatcher broadcastDispatcher,
            BroadcastSender broadcastSender,
            BroadcastSender broadcastSender,
            TimeoutHandler timeoutHandler, UiEventLogger uiEventLogger) {
            TimeoutHandler timeoutHandler,
            FeatureFlags featureFlags,
            ClipboardOverlayUtils clipboardUtils,
            UiEventLogger uiEventLogger) {
        mBroadcastDispatcher = broadcastDispatcher;
        mBroadcastDispatcher = broadcastDispatcher;
        mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
        mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
        final Context displayContext = context.createDisplayContext(getDefaultDisplay());
        final Context displayContext = context.createDisplayContext(getDefaultDisplay());
@@ -199,6 +210,9 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
        mTimeoutHandler = timeoutHandler;
        mTimeoutHandler = timeoutHandler;
        mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);
        mTimeoutHandler.setDefaultTimeoutMillis(CLIPBOARD_DEFAULT_TIMEOUT_MILLIS);


        mFeatureFlags = featureFlags;
        mClipboardUtils = clipboardUtils;

        mView.setCallbacks(mClipboardCallbacks);
        mView.setCallbacks(mClipboardCallbacks);




@@ -257,11 +271,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
        boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
        boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
                && clipData.getDescription().getExtras()
                && clipData.getDescription().getExtras()
                .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
                .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
        boolean isRemote = mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR)
                && mClipboardUtils.isRemoteCopy(mContext, clipData, clipSource);
        if (clipData == null || clipData.getItemCount() == 0) {
        if (clipData == null || clipData.getItemCount() == 0) {
            mView.showDefaultTextPreview();
            mView.showDefaultTextPreview();
        } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
        } else if (!TextUtils.isEmpty(clipData.getItemAt(0).getText())) {
            ClipData.Item item = clipData.getItemAt(0);
            ClipData.Item item = clipData.getItemAt(0);
            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
            if (isRemote || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
                    CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
                    CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
                if (item.getTextLinks() != null) {
                if (item.getTextLinks() != null) {
                    AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
                    AsyncTask.execute(() -> classifyText(clipData.getItemAt(0), clipSource));
@@ -287,7 +303,13 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
        maybeShowRemoteCopy(clipData);
        maybeShowRemoteCopy(clipData);
        animateIn();
        animateIn();
        mView.announceForAccessibility(accessibilityAnnouncement);
        mView.announceForAccessibility(accessibilityAnnouncement);
        mTimeoutHandler.resetTimeout();
        if (isRemote) {
            mTimeoutHandler.cancelTimeout();
            mOnUiUpdate = null;
        } else {
            mOnUiUpdate = mTimeoutHandler::resetTimeout;
            mOnUiUpdate.run();
        }
    }
    }


    private void maybeShowRemoteCopy(ClipData clipData) {
    private void maybeShowRemoteCopy(ClipData clipData) {
@@ -427,7 +449,9 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv
            @Override
            @Override
            public void onAnimationEnd(Animator animation) {
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                super.onAnimationEnd(animation);
                mTimeoutHandler.resetTimeout();
                if (mOnUiUpdate != null) {
                    mOnUiUpdate.run();
                }
            }
            }
        });
        });
        mEnterAnimator.start();
        mEnterAnimator.start();
+46 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.clipboardoverlay;

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ComponentName;
import android.content.Context;

import com.android.systemui.R;

import javax.inject.Inject;

class ClipboardOverlayUtils {

    @Inject
    ClipboardOverlayUtils() {
    }

    boolean isRemoteCopy(Context context, ClipData clipData, String clipSource) {
        if (clipData != null && clipData.getDescription().getExtras() != null
                && clipData.getDescription().getExtras().getBoolean(
                ClipDescription.EXTRA_IS_REMOTE_DEVICE)) {
            ComponentName remoteComponent = ComponentName.unflattenFromString(
                    context.getResources().getString(R.string.config_remoteCopyPackage));
            if (remoteComponent != null) {
                return remoteComponent.getPackageName().equals(clipSource);
            }
        }
        return false;
    }
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -316,6 +316,7 @@ object Flags {


    // 1700 - clipboard
    // 1700 - clipboard
    @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700)
    @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700)
    @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)


    // 1800 - shade container
    // 1800 - shade container
    @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true)
    @JvmField val LEAVE_SHADE_OPEN_FOR_BUGREPORT = UnreleasedFlag(1800, teamfood = true)
+39 −1
Original line number Original line Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.systemui.clipboardoverlay;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHARE_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
@@ -37,6 +39,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.screenshot.TimeoutHandler;
import com.android.systemui.screenshot.TimeoutHandler;


import org.junit.After;
import org.junit.After;
@@ -62,7 +65,10 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
    @Mock
    @Mock
    private TimeoutHandler mTimeoutHandler;
    private TimeoutHandler mTimeoutHandler;
    @Mock
    @Mock
    private ClipboardOverlayUtils mClipboardUtils;
    @Mock
    private UiEventLogger mUiEventLogger;
    private UiEventLogger mUiEventLogger;
    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();


    @Mock
    @Mock
    private Animator mAnimator;
    private Animator mAnimator;
@@ -73,7 +79,6 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
    private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor;
    private ArgumentCaptor<ClipboardOverlayView.ClipboardOverlayCallbacks> mOverlayCallbacksCaptor;
    private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks;
    private ClipboardOverlayView.ClipboardOverlayCallbacks mCallbacks;



    @Before
    @Before
    public void setup() {
    public void setup() {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
@@ -84,6 +89,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
        mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
        mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
                new ClipData.Item("Test Item"));
                new ClipData.Item("Test Item"));


        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, false);

        mOverlayController = new ClipboardOverlayController(
        mOverlayController = new ClipboardOverlayController(
                mContext,
                mContext,
                mClipboardOverlayView,
                mClipboardOverlayView,
@@ -91,6 +98,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
                getFakeBroadcastDispatcher(),
                getFakeBroadcastDispatcher(),
                mBroadcastSender,
                mBroadcastSender,
                mTimeoutHandler,
                mTimeoutHandler,
                mFeatureFlags,
                mClipboardUtils,
                mUiEventLogger);
                mUiEventLogger);
        verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
        verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
        mCallbacks = mOverlayCallbacksCaptor.getValue();
        mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -186,4 +195,33 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase {
        verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
        verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
        verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
        verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
    }
    }

    @Test
    public void test_remoteCopy_withFlagOn() {
        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);

        mOverlayController.setClipData(mSampleClipData, "");

        verify(mTimeoutHandler, never()).resetTimeout();
    }

    @Test
    public void test_remoteCopy_withFlagOff() {
        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);

        mOverlayController.setClipData(mSampleClipData, "");

        verify(mTimeoutHandler).resetTimeout();
    }

    @Test
    public void test_nonRemoteCopy() {
        mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
        when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);

        mOverlayController.setClipData(mSampleClipData, "");

        verify(mTimeoutHandler).resetTimeout();
    }
}
}
+103 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.clipboardoverlay;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.content.ClipData;
import android.content.ClipDescription;
import android.os.PersistableBundle;
import android.testing.TestableResources;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class ClipboardOverlayUtilsTest extends SysuiTestCase {

    private ClipboardOverlayUtils mClipboardUtils;

    @Before
    public void setUp() {
        mClipboardUtils = new ClipboardOverlayUtils();
    }

    @Test
    public void test_extra_withPackage_returnsTrue() {
        PersistableBundle b = new PersistableBundle();
        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
        ClipData data = constructClipData(
                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);
        TestableResources res = mContext.getOrCreateTestableResources();
        res.addOverride(
                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");

        assertTrue(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
    }

    @Test
    public void test_noExtra_returnsFalse() {
        ClipData data = constructClipData(
                new String[]{"text/plain"}, new ClipData.Item("6175550000"), null);
        TestableResources res = mContext.getOrCreateTestableResources();
        res.addOverride(
                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");

        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
    }

    @Test
    public void test_falseExtra_returnsFalse() {
        PersistableBundle b = new PersistableBundle();
        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, false);
        ClipData data = constructClipData(
                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);
        TestableResources res = mContext.getOrCreateTestableResources();
        res.addOverride(
                R.string.config_remoteCopyPackage, "com.android.remote/.RemoteActivity");

        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, "com.android.remote"));
    }

    @Test
    public void test_wrongPackage_returnsFalse() {
        PersistableBundle b = new PersistableBundle();
        b.putBoolean(ClipDescription.EXTRA_IS_REMOTE_DEVICE, true);
        ClipData data = constructClipData(
                new String[]{"text/plain"}, new ClipData.Item("6175550000"), b);

        assertFalse(mClipboardUtils.isRemoteCopy(mContext, data, ""));
    }

    static ClipData constructClipData(String[] mimeTypes, ClipData.Item item,
            PersistableBundle extras) {
        ClipDescription description = new ClipDescription("Test", mimeTypes);
        if (extras != null) {
            description.setExtras(extras);
        }
        return new ClipData(description, item);
    }
}