Add clipboard indication text view
Fork system toast layout to show indication under clipboard overlay and
allow override to provide different indication text.
Bug: 361199935
Flag: com.android.systemui.show_clipboard_indication
Test: atest ClipboardOverlayControllerTest
Change-Id: I6e38d386e94fdbcbaf4101676aec6abdead2c3bb
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 5c5edb1..b2ea6aa 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1559,6 +1559,13 @@
}
flag {
+ name: "show_clipboard_indication"
+ namespace: "systemui"
+ description: "Show indication text under the clipboard overlay when copied something"
+ bug: "361199935"
+}
+
+flag {
name: "media_projection_dialog_behind_lockscreen"
namespace: "systemui"
description: "Ensure MediaProjection Dialog appears behind the lockscreen"
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 65005f8..572f063 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -32,6 +32,34 @@
android:id="@+id/min_edge_guideline"
app:layout_constraintGuide_begin="@dimen/overlay_action_container_minimum_edge_spacing"
android:orientation="vertical"/>
+ <!-- This toast-like indication layout was forked from text_toast.xml and will have the same
+ appearance as system toast. -->
+ <FrameLayout
+ android:id="@+id/indication_container"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:maxWidth="@*android:dimen/toast_width"
+ android:background="@android:drawable/toast_frame"
+ android:elevation="@*android:dimen/toast_elevation"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+ <TextView
+ android:id="@+id/indication_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:textAppearance="@*android:style/TextAppearance.Toast"/>
+ </FrameLayout>
<!-- Negative horizontal margin because this container background must render beyond the thing
it's constrained by (the actions themselves). -->
<FrameLayout
@@ -47,7 +75,7 @@
app:layout_constraintStart_toStartOf="@id/min_edge_guideline"
app:layout_constraintTop_toTopOf="@id/actions_container"
app:layout_constraintEnd_toEndOf="@id/actions_container"
- app:layout_constraintBottom_toBottomOf="parent"/>
+ app:layout_constraintBottom_toTopOf="@id/indication_container"/>
<HorizontalScrollView
android:id="@+id/actions_container"
android:layout_width="0dp"
@@ -144,7 +172,7 @@
android:visibility="gone"
android:elevation="7dp"
android:padding="8dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/indication_container"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
new file mode 100644
index 0000000..ddd6bc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationCallback.kt
@@ -0,0 +1,24 @@
+/*
+ * 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
+
+/** Interface for listening to indication text changed from [ClipboardIndicationProvider]. */
+interface ClipboardIndicationCallback {
+
+ /** Notifies the indication text changed. */
+ fun onIndicationTextChanged(text: CharSequence)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
new file mode 100644
index 0000000..be32723
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * 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
+
+/** Interface to provide the clipboard indication to be shown under the overlay. */
+interface ClipboardIndicationProvider {
+
+ /**
+ * Gets the indication text async.
+ *
+ * @param callback callback for getting the indication text.
+ */
+ fun getIndicationText(callback: ClipboardIndicationCallback)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
new file mode 100644
index 0000000..da94d5b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardIndicationProviderImpl.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+open class ClipboardIndicationProviderImpl @Inject constructor() : ClipboardIndicationProvider {
+
+ override fun getIndicationText(callback: ClipboardIndicationCallback) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 65c01ed..ac74784 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -21,6 +21,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
import static com.android.systemui.Flags.clipboardImageTimeout;
import static com.android.systemui.Flags.clipboardSharedTransitions;
+import static com.android.systemui.Flags.showClipboardIndication;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
@@ -99,6 +100,7 @@
private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
+ private final ClipboardIndicationProvider mClipboardIndicationProvider;
private Runnable mOnSessionCompleteListener;
private Runnable mOnRemoteCopyTapped;
@@ -173,6 +175,13 @@
}
};
+ private ClipboardIndicationCallback mIndicationCallback = new ClipboardIndicationCallback() {
+ @Override
+ public void onIndicationTextChanged(@NonNull CharSequence text) {
+ mView.setIndicationText(text);
+ }
+ };
+
@Inject
public ClipboardOverlayController(@OverlayWindowContext Context context,
ClipboardOverlayView clipboardOverlayView,
@@ -185,11 +194,13 @@
@Background Executor bgExecutor,
ClipboardImageLoader clipboardImageLoader,
ClipboardTransitionExecutor transitionExecutor,
+ ClipboardIndicationProvider clipboardIndicationProvider,
UiEventLogger uiEventLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mClipboardImageLoader = clipboardImageLoader;
mTransitionExecutor = transitionExecutor;
+ mClipboardIndicationProvider = clipboardIndicationProvider;
mClipboardLogger = new ClipboardLogger(uiEventLogger);
@@ -288,6 +299,9 @@
boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
mClipboardModel = model;
mClipboardLogger.setClipSource(mClipboardModel.getSource());
+ if (showClipboardIndication()) {
+ mClipboardIndicationProvider.getIndicationText(mIndicationCallback);
+ }
if (clipboardImageTimeout()) {
if (shouldAnimate) {
reset();
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 1762d82..7e4d762 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -18,6 +18,8 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.systemui.Flags.showClipboardIndication;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -53,6 +55,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -103,6 +106,8 @@
private View mShareChip;
private View mRemoteCopyChip;
private View mActionContainerBackground;
+ private View mIndicationContainer;
+ private TextView mIndicationText;
private View mDismissButton;
private LinearLayout mActionContainer;
private ClipboardOverlayCallbacks mClipboardCallbacks;
@@ -136,6 +141,8 @@
mShareChip = requireViewById(R.id.share_chip);
mRemoteCopyChip = requireViewById(R.id.remote_copy_chip);
mDismissButton = requireViewById(R.id.dismiss_button);
+ mIndicationContainer = requireViewById(R.id.indication_container);
+ mIndicationText = mIndicationContainer.findViewById(R.id.indication_text);
bindDefaultActionChips();
@@ -208,6 +215,14 @@
}
}
+ void setIndicationText(CharSequence text) {
+ mIndicationText.setText(text);
+
+ // Set the visibility of clipboard indication based on the text is empty or not.
+ int visibility = text.isEmpty() ? View.GONE : View.VISIBLE;
+ mIndicationContainer.setVisibility(visibility);
+ }
+
void setMinimized(boolean minimized) {
if (minimized) {
mMinimizedPreview.setVisibility(View.VISIBLE);
@@ -221,6 +236,18 @@
mPreviewBorder.setVisibility(View.VISIBLE);
mActionContainer.setVisibility(View.VISIBLE);
}
+
+ if (showClipboardIndication()) {
+ // Adjust the margin of clipboard indication based on the minimized state.
+ int marginStart = minimized ? getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_margin_horizontal)
+ : getResources().getDimensionPixelSize(
+ R.dimen.overlay_action_container_minimum_edge_spacing);
+ ConstraintLayout.LayoutParams params =
+ (ConstraintLayout.LayoutParams) mIndicationContainer.getLayoutParams();
+ params.setMarginStart(marginStart);
+ mIndicationContainer.setLayoutParams(params);
+ }
}
void setInsets(WindowInsets insets, int orientation) {
@@ -313,6 +340,7 @@
setTranslationX(0);
setAlpha(0);
mActionContainerBackground.setVisibility(View.GONE);
+ mIndicationContainer.setVisibility(View.GONE);
mDismissButton.setVisibility(View.GONE);
mShareChip.setVisibility(View.GONE);
mRemoteCopyChip.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
rename to packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
index 527819c..c81f0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlaySuppressionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayOverrideModule.kt
@@ -15,18 +15,26 @@
*/
package com.android.systemui.clipboardoverlay.dagger
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProvider
+import com.android.systemui.clipboardoverlay.ClipboardIndicationProviderImpl
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. */
+/** Dagger Module to provide default implementations which could be overridden. */
@Module
-interface ClipboardOverlaySuppressionModule {
+interface ClipboardOverlayOverrideModule {
/** Provides implementation for [ClipboardOverlaySuppressionController]. */
@Binds
fun provideClipboardOverlaySuppressionController(
clipboardOverlaySuppressionControllerImpl: ClipboardOverlaySuppressionControllerImpl
): ClipboardOverlaySuppressionController
+
+ /** Provides implementation for [ClipboardIndicationProvider]. */
+ @Binds
+ fun provideClipboardIndicationProvider(
+ clipboardIndicationProviderImpl: ClipboardIndicationProviderImpl
+ ): ClipboardIndicationProvider
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 609b733..2e323d4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -29,7 +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.clipboardoverlay.dagger.ClipboardOverlayOverrideModule;
import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -125,7 +125,7 @@
AospPolicyModule.class,
BatterySaverModule.class,
CentralSurfacesModule.class,
- ClipboardOverlaySuppressionModule.class,
+ ClipboardOverlayOverrideModule.class,
CollapsedStatusBarFragmentStartableModule.class,
ConnectingDisplayViewModel.StartableModule.class,
DefaultBlueprintModule.class,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 6061063..5624815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -20,6 +20,7 @@
import static com.android.systemui.Flags.FLAG_CLIPBOARD_SHARED_TRANSITIONS;
import static com.android.systemui.Flags.FLAG_CLIPBOARD_USE_DESCRIPTION_MIMETYPE;
+import static com.android.systemui.Flags.FLAG_SHOW_CLIPBOARD_INDICATION;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED;
@@ -121,6 +122,24 @@
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private class FakeClipboardIndicationProvider implements ClipboardIndicationProvider {
+ private ClipboardIndicationCallback mIndicationCallback;
+
+ public void notifyIndicationTextChanged(CharSequence indicationText) {
+ if (mIndicationCallback != null) {
+ mIndicationCallback.onIndicationTextChanged(indicationText);
+ }
+ }
+
+ @Override
+ public void getIndicationText(ClipboardIndicationCallback callback) {
+ mIndicationCallback = callback;
+ }
+ }
+
+ private FakeClipboardIndicationProvider mClipboardIndicationProvider =
+ new FakeClipboardIndicationProvider();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -156,6 +175,7 @@
mExecutor,
mClipboardImageLoader,
mClipboardTransitionExecutor,
+ mClipboardIndicationProvider,
mUiEventLogger);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -305,6 +325,17 @@
}
@Test
+ @EnableFlags(FLAG_SHOW_CLIPBOARD_INDICATION)
+ public void test_onIndicationTextChanged_setIndicationTextCorrectly() {
+ initController();
+ mOverlayController.setClipData(mSampleClipData, "");
+
+ mClipboardIndicationProvider.notifyIndicationTextChanged("copied");
+
+ verify(mClipboardOverlayView).setIndicationText("copied");
+ }
+
+ @Test
@DisableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() {
initController();