From da69661adda9b03bc986f54d4b54bae04e3f9e46 Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Fri, 17 Feb 2023 15:06:58 +0000 Subject: [PATCH] Show tooltips on cursor hover of taskbar icons. Fix: 250092437 Test: TaskbarHoverToolTipControllerTest Flag: ENABLE_CURSOR_HOVER_STATES Change-Id: Ia0463518d13b313b354328078685930215fb7636 --- quickstep/res/values/styles.xml | 5 + .../TaskbarAutohideSuspendController.java | 3 + .../TaskbarHoverToolTipController.java | 175 ++++++++++++++ .../launcher3/taskbar/TaskbarView.java | 11 + .../taskbar/TaskbarViewController.java | 5 + .../TaskbarHoverToolTipControllerTest.java | 221 ++++++++++++++++++ .../arrow_toast_rounded_background.xml | 2 +- res/layout/arrow_toast.xml | 2 +- res/values/attrs.xml | 1 + res/values/styles.xml | 1 + .../launcher3/AbstractFloatingView.java | 2 + .../android/launcher3/views/ArrowTipView.java | 37 +++ tests/Android.bp | 1 + 13 files changed, 464 insertions(+), 2 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java create mode 100644 quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index 21b7fd5139..fc0370421d 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -298,4 +298,9 @@ + + diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java index 70999e7131..8ab2ffadcf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java @@ -45,6 +45,8 @@ public class TaskbarAutohideSuspendController implements public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4; // Transient Taskbar is temporarily unstashed (pending a timeout). public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5; + // User has hovered the taskbar. + public static final int FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS = 1 << 6; @IntDef(flag = true, value = { FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, @@ -53,6 +55,7 @@ public class TaskbarAutohideSuspendController implements FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, + FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, }) @Retention(RetentionPolicy.SOURCE) public @interface AutohideSuspendFlag {} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java new file mode 100644 index 0000000000..363f915e95 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2023 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.launcher3.taskbar; + +import static android.view.MotionEvent.ACTION_HOVER_ENTER; +import static android.view.MotionEvent.ACTION_HOVER_EXIT; +import static android.view.View.ALPHA; +import static android.view.View.SCALE_Y; +import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT; + +import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_EXCEPT_ON_BOARD_POPUP; +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; +import static com.android.launcher3.views.ArrowTipView.TEXT_ALPHA; + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.view.ContextThemeWrapper; +import android.view.MotionEvent; +import android.view.View; + +import com.android.app.animation.Interpolators; +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.views.ArrowTipView; + +/** + * Controls showing a tooltip in the taskbar above each icon when it is hovered. + */ +public class TaskbarHoverToolTipController implements View.OnHoverListener { + + private static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400; + private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300; + private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150; + + private final Handler mHoverToolTipHandler = new Handler(Looper.getMainLooper()); + private final Runnable mRevealHoverToolTipRunnable = this::revealHoverToolTip; + private final Runnable mHideHoverToolTipRunnable = this::hideHoverToolTip; + + private final TaskbarActivityContext mActivity; + private final TaskbarView mTaskbarView; + private final View mHoverView; + private final ArrowTipView mHoverToolTipView; + private final String mToolTipText; + + public TaskbarHoverToolTipController(TaskbarActivityContext activity, TaskbarView taskbarView, + View hoverView) { + mActivity = activity; + mTaskbarView = taskbarView; + mHoverView = hoverView; + + if (mHoverView instanceof BubbleTextView) { + mToolTipText = ((BubbleTextView) mHoverView).getText().toString(); + } else if (mHoverView instanceof FolderIcon + && ((FolderIcon) mHoverView).mInfo.title != null) { + mToolTipText = ((FolderIcon) mHoverView).mInfo.title.toString(); + } else { + mToolTipText = null; + } + + ContextThemeWrapper arrowContextWrapper = new ContextThemeWrapper(mActivity, + R.style.ArrowTipTaskbarStyle); + mHoverToolTipView = new ArrowTipView(arrowContextWrapper, /* isPointingUp = */ false, + R.layout.arrow_toast); + + AnimatorSet hoverCloseAnimator = new AnimatorSet(); + ObjectAnimator textCloseAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0); + textCloseAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0, 0.33f)); + ObjectAnimator alphaCloseAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 0); + alphaCloseAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 0.66f)); + ObjectAnimator scaleCloseAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 0); + scaleCloseAnimator.setInterpolator(Interpolators.STANDARD); + hoverCloseAnimator.playTogether( + textCloseAnimator, + alphaCloseAnimator, + scaleCloseAnimator); + hoverCloseAnimator.setStartDelay(0); + hoverCloseAnimator.setDuration(HOVER_TOOL_TIP_EXIT_DURATION); + mHoverToolTipView.setCustomCloseAnimation(hoverCloseAnimator); + + AnimatorSet hoverOpenAnimator = new AnimatorSet(); + ObjectAnimator textOpenAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 255); + textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 1f)); + ObjectAnimator scaleOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 1f); + scaleOpenAnimator.setInterpolator(Interpolators.EMPHASIZED); + ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 1f); + alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.1f, 0.33f)); + hoverOpenAnimator.playTogether( + scaleOpenAnimator, + textOpenAnimator, + alphaOpenAnimator); + hoverOpenAnimator.setStartDelay(HOVER_TOOL_TIP_REVEAL_START_DELAY); + hoverOpenAnimator.setDuration(HOVER_TOOL_TIP_REVEAL_DURATION); + mHoverToolTipView.setCustomOpenAnimation(hoverOpenAnimator); + + mHoverToolTipView.addOnLayoutChangeListener( + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + mHoverToolTipView.setPivotY(bottom); + mHoverToolTipView.setY(mTaskbarView.getTop() - (bottom - top)); + }); + mHoverToolTipView.setScaleY(0f); + mHoverToolTipView.setAlpha(0f); + } + + @Override + public boolean onHover(View v, MotionEvent event) { + boolean isAnyOtherFloatingViewOpen = + AbstractFloatingView.hasOpenView(mActivity, TYPE_ALL_EXCEPT_ON_BOARD_POPUP); + if (isAnyOtherFloatingViewOpen) { + mHoverToolTipHandler.removeCallbacksAndMessages(null); + } + // If hover leaves a taskbar icon animate the tooltip closed. + if (event.getAction() == ACTION_HOVER_EXIT) { + startHideHoverToolTip(); + mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, false); + return true; + } else if (!isAnyOtherFloatingViewOpen && event.getAction() == ACTION_HOVER_ENTER) { + // If hovering above a taskbar icon starts, animate the tooltip open. Do not + // reveal if any floating views such as folders or edu pop-ups are open. + startRevealHoverToolTip(); + mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); + return true; + } + return false; + } + + private void startRevealHoverToolTip() { + mActivity.setTaskbarWindowFullscreen(true); + mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable, + HOVER_TOOL_TIP_REVEAL_START_DELAY); + } + + private void revealHoverToolTip() { + if (mHoverView == null || mToolTipText == null) { + return; + } + if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) { + return; + } + Rect iconViewBounds = Utilities.getViewBounds(mHoverView); + mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(), + mTaskbarView.getTop(), /* shouldAutoClose= */ false); + } + + private void startHideHoverToolTip() { + mHoverToolTipHandler.removeCallbacks(mRevealHoverToolTipRunnable); + int accessibilityHideTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis( + mActivity, /* originalTimeout= */ 0, FLAG_CONTENT_TEXT); + mHoverToolTipHandler.postDelayed(mHideHoverToolTipRunnable, accessibilityHideTimeout); + } + + private void hideHoverToolTip() { + mHoverToolTipView.close(/* animate = */ true); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index bf3b932424..074cbe116e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar; import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import android.content.Context; @@ -319,6 +320,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } } setClickAndLongClickListenersForIcon(hotseatView); + if (ENABLE_CURSOR_HOVER_STATES.get()) { + setHoverListenerForIcon(hotseatView); + } nextViewIndex++; } // Remove remaining views @@ -366,6 +370,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar icon.setOnLongClickListener(mIconLongClickListener); } + /** + * Sets OnHoverListener for the given view. + */ + private void setHoverListenerForIcon(View icon) { + icon.setOnHoverListener(mControllerCallbacks.getIconOnHoverListener(icon)); + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int count = getChildCount(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 1b454044db..3d22e7895f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -690,6 +690,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar .updateAndAnimateIsManuallyStashedInApp(true); } + /** Gets the hover listener for the provided icon view. */ + public View.OnHoverListener getIconOnHoverListener(View icon) { + return new TaskbarHoverToolTipController(mActivity, mTaskbarView, icon); + } + /** * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to * consume the touch so TaskbarView treats it as an ACTION_CANCEL. diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java new file mode 100644 index 0000000000..82849be26e --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2023 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.launcher3.taskbar; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.Display; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.util.ActivityContextWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +/** + * Tests for TaskbarHoverToolTipController. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { + + private TaskbarHoverToolTipController mTaskbarHoverToolTipController; + private TestableLooper mTestableLooper; + + @Mock private TaskbarView mTaskbarView; + @Mock private MotionEvent mMotionEvent; + @Mock private BubbleTextView mHoverBubbleTextView; + @Mock private FolderIcon mHoverFolderIcon; + @Mock private Display mDisplay; + @Mock private TaskbarDragLayer mTaskbarDragLayer; + private Folder mSpyFolderView; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + Context context = getApplicationContext(); + + doAnswer((Answer) invocation -> context.getSystemService( + (String) invocation.getArgument(0))) + .when(taskbarActivityContext).getSystemService(anyString()); + when(taskbarActivityContext.getResources()).thenReturn(context.getResources()); + when(taskbarActivityContext.getApplicationInfo()).thenReturn( + context.getApplicationInfo()); + when(taskbarActivityContext.getDragLayer()).thenReturn(mTaskbarDragLayer); + when(taskbarActivityContext.getMainLooper()).thenReturn(context.getMainLooper()); + when(taskbarActivityContext.getDisplay()).thenReturn(mDisplay); + + when(mTaskbarDragLayer.getChildCount()).thenReturn(1); + mSpyFolderView = spy(new Folder(new ActivityContextWrapper(context), null)); + when(mTaskbarDragLayer.getChildAt(anyInt())).thenReturn(mSpyFolderView); + doReturn(false).when(mSpyFolderView).isOpen(); + + when(mHoverBubbleTextView.getText()).thenReturn("tooltip"); + doAnswer((Answer) invocation -> { + Object[] args = invocation.getArguments(); + ((int[]) args[0])[0] = 0; + ((int[]) args[0])[1] = 0; + return null; + }).when(mHoverBubbleTextView).getLocationOnScreen(any(int[].class)); + when(mHoverBubbleTextView.getWidth()).thenReturn(100); + when(mHoverBubbleTextView.getHeight()).thenReturn(100); + + mHoverFolderIcon.mInfo = new FolderInfo(); + mHoverFolderIcon.mInfo.title = "tooltip"; + doAnswer((Answer) invocation -> { + Object[] args = invocation.getArguments(); + ((int[]) args[0])[0] = 0; + ((int[]) args[0])[1] = 0; + return null; + }).when(mHoverFolderIcon).getLocationOnScreen(any(int[].class)); + when(mHoverFolderIcon.getWidth()).thenReturn(100); + when(mHoverFolderIcon.getHeight()).thenReturn(100); + + when(mTaskbarView.getTop()).thenReturn(200); + + mTaskbarHoverToolTipController = new TaskbarHoverToolTipController( + taskbarActivityContext, mTaskbarView, mHoverBubbleTextView); + mTestableLooper = TestableLooper.get(this); + } + + @Test + public void onHover_hoverEnterIcon_revealToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + true); + verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); + } + + @Test + public void onHover_hoverExitIcon_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverEnterFolderIcon_revealToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + true); + verify(taskbarActivityContext).setTaskbarWindowFullscreen(true); + } + + @Test + public void onHover_hoverExitFolderIcon_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverExitFolderOpen_closeToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT); + doReturn(true).when(mSpyFolderView).isOpen(); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + waitForIdleSync(); + + assertThat(hoverHandled).isTrue(); + verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, + false); + } + + @Test + public void onHover_hoverEnterFolderOpen_noToolTip() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER); + doReturn(true).when(mSpyFolderView).isOpen(); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + + assertThat(hoverHandled).isFalse(); + } + + @Test + public void onHover_hoverMove_noUpdate() { + when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_MOVE); + when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_MOVE); + + boolean hoverHandled = + mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); + + assertThat(hoverHandled).isFalse(); + } + + private void waitForIdleSync() { + mTestableLooper.processAllMessages(); + } +} diff --git a/res/drawable/arrow_toast_rounded_background.xml b/res/drawable/arrow_toast_rounded_background.xml index 1206ddd87b..d7d625567b 100644 --- a/res/drawable/arrow_toast_rounded_background.xml +++ b/res/drawable/arrow_toast_rounded_background.xml @@ -14,6 +14,6 @@ limitations under the License. --> - + diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml index 88a92eb565..004e77833a 100644 --- a/res/layout/arrow_toast.xml +++ b/res/layout/arrow_toast.xml @@ -28,7 +28,7 @@ android:padding="16dp" android:background="@drawable/arrow_toast_rounded_background" android:elevation="@dimen/arrow_toast_elevation" - android:textColor="@color/arrow_tip_view_content" + android:textColor="?attr/arrowTipTextColor" android:textSize="14sp"/> + diff --git a/res/values/styles.xml b/res/values/styles.xml index 14454bdedb..1695c58c19 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -425,5 +425,6 @@ diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 31f9bfea4a..b845c88d23 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -133,6 +133,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_TASKBAR_OVERLAYS = TYPE_TASKBAR_ALL_APPS | TYPE_TASKBAR_EDUCATION_DIALOG; + public static final int TYPE_ALL_EXCEPT_ON_BOARD_POPUP = TYPE_ALL & ~TYPE_ON_BOARD_POPUP; + protected boolean mIsOpen; public AbstractFloatingView(Context context, AttributeSet attrs) { diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index 8e056501d5..b44dbeb616 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -28,7 +28,9 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.Handler; +import android.util.IntProperty; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -43,6 +45,7 @@ import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.graphics.TriangleShape; @@ -57,6 +60,19 @@ public class ArrowTipView extends AbstractFloatingView { private static final long SHOW_DURATION_MS = 300; private static final long HIDE_DURATION_MS = 100; + public static final IntProperty TEXT_ALPHA = + new IntProperty<>("textAlpha") { + @Override + public void setValue(ArrowTipView view, int v) { + view.setTextAlpha(v); + } + + @Override + public Integer get(ArrowTipView view) { + return view.getTextAlpha(); + } + }; + private final ActivityContext mActivityContext; private final Handler mHandler = new Handler(); private boolean mIsPointingUp; @@ -69,6 +85,8 @@ public class ArrowTipView extends AbstractFloatingView { private AnimatorSet mOpenAnimator = new AnimatorSet(); private AnimatorSet mCloseAnimator = new AnimatorSet(); + private int mTextAlpha; + public ArrowTipView(Context context) { this(context, false); } @@ -86,6 +104,11 @@ public class ArrowTipView extends AbstractFloatingView { mArrowMinOffset = context.getResources().getDimensionPixelSize( R.dimen.dynamic_grid_cell_border_spacing); TypedArray ta = context.obtainStyledAttributes(R.styleable.ArrowTipView); + // Set style to default to avoid inflation issues with missing attributes. + if (!ta.hasValue(R.styleable.ArrowTipView_arrowTipBackground) + || !ta.hasValue(R.styleable.ArrowTipView_arrowTipTextColor)) { + context = new ContextThemeWrapper(context, R.style.ArrowTipStyle); + } mArrowViewPaintColor = ta.getColor(R.styleable.ArrowTipView_arrowTipBackground, context.getColor(R.color.arrow_tip_view_bg)); ta.recycle(); @@ -110,6 +133,8 @@ public class ArrowTipView extends AbstractFloatingView { } if (mIsOpen) { if (animate) { + mCloseAnimator.addListener(AnimatorListeners.forSuccessCallback( + () -> mActivityContext.getDragLayer().removeView(this))); mCloseAnimator.start(); } else { mCloseAnimator.cancel(); @@ -414,4 +439,16 @@ public class ArrowTipView extends AbstractFloatingView { public void setCustomCloseAnimation(AnimatorSet animator) { mCloseAnimator = animator; } + + private void setTextAlpha(int textAlpha) { + if (mTextAlpha != textAlpha) { + mTextAlpha = textAlpha; + TextView textView = findViewById(R.id.text); + textView.setTextColor(textView.getTextColors().withAlpha(mTextAlpha)); + } + } + + private int getTextAlpha() { + return mTextAlpha; + } } diff --git a/tests/Android.bp b/tests/Android.bp index e7f408487d..b32d87cd3b 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -87,6 +87,7 @@ android_library { "launcher_log_protos_lite", "truth-prebuilt", "platform-test-rules", + "testables", ], manifest: "AndroidManifest-common.xml", platform_apis: true,