Allow override the suppress Overlay condition
Create interface for allowing the #shouldSuppressOverlay() method could
be overrided by different implementation.
Bug: 358473717
Flag: com.android.systemui.override_suppress_overlay_condition
Test: atest ClipboardListenerTest
Change-Id: Iaa3b518820a923537968c4c67cf047bcb77b9b50
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a9d4c89..d98b05d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1424,6 +1424,13 @@
}
flag {
+ name: "override_suppress_overlay_condition"
+ namespace: "systemui"
+ description: "Allow override the conditions to suppress the clipboard overlay"
+ bug: "358473717"
+}
+
+flag {
name: "media_projection_dialog_behind_lockscreen"
namespace: "systemui"
description: "Ensure MediaProjection Dialog appears behind the lockscreen"
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 1ada56d..7033e64 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 @@
import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen;
+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;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN;
@@ -63,6 +64,7 @@
private final ClipboardManager mClipboardManager;
private final KeyguardManager mKeyguardManager;
private final UiEventLogger mUiEventLogger;
+ private final ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController;
private ClipboardOverlay mClipboardOverlay;
@Inject
@@ -71,13 +73,15 @@
ClipboardToast clipboardToast,
UserScopedService<ClipboardManager> clipboardManager,
KeyguardManager keyguardManager,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ ClipboardOverlaySuppressionController clipboardOverlaySuppressionController) {
mContext = context;
mOverlayProvider = clipboardOverlayControllerProvider;
mClipboardToast = clipboardToast;
mClipboardManager = clipboardManager.forUser(UserHandle.CURRENT);
mKeyguardManager = keyguardManager;
mUiEventLogger = uiEventLogger;
+ mClipboardOverlaySuppressionController = clipboardOverlaySuppressionController;
}
@Override
@@ -94,9 +98,17 @@
String clipSource = mClipboardManager.getPrimaryClipSource();
ClipData clipData = mClipboardManager.getPrimaryClip();
- if (shouldSuppressOverlay(clipData, clipSource, Build.IS_EMULATOR)) {
- Log.i(TAG, "Clipboard overlay suppressed.");
- return;
+ if (overrideSuppressOverlayCondition()) {
+ if (mClipboardOverlaySuppressionController.shouldSuppressOverlay(clipData, clipSource,
+ Build.IS_EMULATOR)) {
+ Log.i(TAG, "Clipboard overlay suppressed.");
+ return;
+ }
+ } else {
+ if (shouldSuppressOverlay(clipData, clipSource, Build.IS_EMULATOR)) {
+ Log.i(TAG, "Clipboard overlay suppressed.");
+ return;
+ }
}
// user should not access intents before setup or while device is locked
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionController.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionController.kt
new file mode 100644
index 0000000..be592da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionController.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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
+
+/** Interface to control the clipboard overlay suppression behavior. */
+interface ClipboardOverlaySuppressionController {
+
+ /** Decides if the clipboard overlay should be suppressed. */
+ fun shouldSuppressOverlay(
+ clipData: ClipData?,
+ clipSource: String?,
+ isEmulator: Boolean,
+ ): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImpl.kt
new file mode 100644
index 0000000..814341d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImpl.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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 com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+open class ClipboardOverlaySuppressionControllerImpl @Inject constructor() :
+ ClipboardOverlaySuppressionController {
+
+ // The overlay is suppressed if EXTRA_SUPPRESS_OVERLAY is true and the device is an emulator or
+ // the source package is SHELL_PACKAGE. This is meant to suppress the overlay when the emulator
+ // or a mirrored device is syncing the clipboard.
+ override fun shouldSuppressOverlay(
+ clipData: ClipData?,
+ clipSource: String?,
+ isEmulator: Boolean,
+ ): Boolean {
+ if (!(isEmulator || SHELL_PACKAGE == clipSource)) {
+ return false
+ }
+ if (clipData == null || clipData.description.extras == null) {
+ return false
+ }
+ return clipData.description.extras.getBoolean(EXTRA_SUPPRESS_OVERLAY, false)
+ }
+
+ companion object {
+ @VisibleForTesting const val SHELL_PACKAGE = "com.android.shell"
+
+ @VisibleForTesting
+ const val EXTRA_SUPPRESS_OVERLAY = "com.android.systemui.SUPPRESS_CLIPBOARD_OVERLAY"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
new file mode 100644
index 0000000..527819c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.dagger
+
+import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionController
+import com.android.systemui.clipboardoverlay.ClipboardOverlaySuppressionControllerImpl
+import dagger.Binds
+import dagger.Module
+
+/** Dagger Module for code in the clipboard overlay package. */
+@Module
+interface ClipboardOverlaySuppressionModule {
+
+ /** Provides implementation for [ClipboardOverlaySuppressionController]. */
+ @Binds
+ fun provideClipboardOverlaySuppressionController(
+ clipboardOverlaySuppressionControllerImpl: ClipboardOverlaySuppressionControllerImpl
+ ): ClipboardOverlaySuppressionController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 5c075c2..a94fbd9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -29,6 +29,7 @@
import com.android.systemui.accessibility.SystemActionsModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
import com.android.systemui.battery.BatterySaverModule;
+import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlaySuppressionModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -116,6 +117,7 @@
AccessibilityRepositoryModule.class,
AospPolicyModule.class,
BatterySaverModule.class,
+ ClipboardOverlaySuppressionModule.class,
CollapsedStatusBarFragmentStartableModule.class,
ConnectingDisplayViewModel.StartableModule.class,
DefaultBlueprintModule.class,
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 d72b72c..a2c36ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -31,6 +32,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
+import android.os.Build;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
@@ -71,6 +73,8 @@
private ClipboardToast mClipboardToast;
@Mock
private UiEventLogger mUiEventLogger;
+ @Mock
+ private ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController;
private ClipData mSampleClipData;
private String mSampleSource = "Example source";
@@ -113,7 +117,8 @@
return null;
},
mKeyguardManager,
- mUiEventLogger);
+ mUiEventLogger,
+ mClipboardOverlaySuppressionController);
}
@@ -155,6 +160,7 @@
}
@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
assertFalse(ClipboardListener.shouldSuppressOverlay(mSampleClipData, mSampleSource,
@@ -236,6 +242,64 @@
}
@Test
+ @EnableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION)
+ public void test_onPrimaryClipChanged_notSuppressOverlay() {
+ when(mClipboardOverlaySuppressionController.shouldSuppressOverlay(any(), any(),
+ anyBoolean())).thenReturn(false);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mClipboardOverlaySuppressionController).shouldSuppressOverlay(mSampleClipData,
+ mSampleSource, Build.IS_EMULATOR);
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
+ verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION)
+ public void test_onPrimaryClipChanged_suppressOverlay() {
+ when(mClipboardOverlaySuppressionController.shouldSuppressOverlay(any(), any(),
+ anyBoolean())).thenReturn(true);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mClipboardOverlaySuppressionController).shouldSuppressOverlay(mSampleClipData,
+ mSampleSource, Build.IS_EMULATOR);
+ verifyZeroInteractions(mOverlayControllerProvider);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION)
+ public void test_onPrimaryClipChanged_legacyBehavior_notSuppressOverlay() {
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
+ verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION)
+ public void test_onPrimaryClipChanged_legacyBehavior_suppressOverlay() {
+ ClipDescription desc = new ClipDescription("Test", new String[]{"text/plain"});
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(ClipboardListener.EXTRA_SUPPRESS_OVERLAY, true);
+ desc.setExtras(bundle);
+ ClipData suppressableClipData = new ClipData(desc, new ClipData.Item("Test Item"));
+ when(mClipboardManager.getPrimaryClip()).thenReturn(suppressableClipData);
+ when(mClipboardManager.getPrimaryClipSource()).thenReturn(ClipboardListener.SHELL_PACKAGE);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verifyZeroInteractions(mOverlayControllerProvider);
+ }
+
+ @Test
public void test_nullClipData_showsNothing() {
when(mClipboardManager.getPrimaryClip()).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt
new file mode 100644
index 0000000..86788d3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlaySuppressionControllerImplTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.os.PersistableBundle
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ClipboardOverlaySuppressionControllerImplTest : SysuiTestCase() {
+ private lateinit var mSampleClipData: ClipData
+ private lateinit var mSuppressableClipData: ClipData
+ private lateinit var mClipboardOverlaySuppressionControllerImpl:
+ ClipboardOverlaySuppressionControllerImpl
+
+ @Before
+ fun setup() {
+ mSampleClipData = ClipData("Test", arrayOf("text/plain"), ClipData.Item("Test Item"))
+
+ val desc = ClipDescription("Test", arrayOf("text/plain"))
+ val bundle = PersistableBundle()
+ bundle.putBoolean(ClipboardOverlaySuppressionControllerImpl.EXTRA_SUPPRESS_OVERLAY, true)
+ desc.extras = bundle
+ mSuppressableClipData = ClipData(desc, ClipData.Item("Test Item"))
+
+ mClipboardOverlaySuppressionControllerImpl = ClipboardOverlaySuppressionControllerImpl()
+ }
+
+ @Test
+ fun shouldSuppressOverlay_notEmulatorOrShellPackage_returnFalse() {
+ Assert.assertFalse(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ mSuppressableClipData,
+ EXAMPLE_PACKAGE,
+ false,
+ )
+ )
+ }
+
+ @Test
+ fun shouldSuppressOverlay_nullClipData_returnFalse() {
+ Assert.assertFalse(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ null,
+ ClipboardOverlaySuppressionControllerImpl.SHELL_PACKAGE,
+ true,
+ )
+ )
+ }
+
+ @Test
+ fun shouldSuppressOverlay_noSuppressOverlayExtra_returnFalse() {
+ // Regardless of the package or emulator, nothing should be suppressed without the flag.
+ Assert.assertFalse(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ mSampleClipData,
+ EXAMPLE_PACKAGE,
+ true,
+ )
+ )
+ Assert.assertFalse(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ mSampleClipData,
+ ClipboardOverlaySuppressionControllerImpl.SHELL_PACKAGE,
+ false,
+ )
+ )
+ }
+
+ @Test
+ fun shouldSuppressOverlay_hasSuppressOverlayExtra_returnTrue() {
+ // Clip data with the suppression extra is only honored in the emulator or with the shell
+ // package.
+ Assert.assertTrue(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ mSuppressableClipData,
+ EXAMPLE_PACKAGE,
+ true,
+ )
+ )
+ Assert.assertTrue(
+ mClipboardOverlaySuppressionControllerImpl.shouldSuppressOverlay(
+ mSuppressableClipData,
+ ClipboardOverlaySuppressionControllerImpl.SHELL_PACKAGE,
+ false,
+ )
+ )
+ }
+
+ companion object {
+ const val EXAMPLE_PACKAGE = "com.example"
+ }
+}