Merge "Not show hover border on select mode" into main
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 95ce406..35fa539 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -238,6 +238,12 @@
if (toState == MODAL_TASK) {
setOverviewSelectEnabled(true);
}
+
+ // Set border after select mode changes to avoid showing border during state transition
+ if (!toState.overviewUi() || toState == MODAL_TASK) {
+ setTaskBorderEnabled(false);
+ }
+
setFreezeViewVisibility(true);
}
@@ -253,6 +259,11 @@
if (finalState != MODAL_TASK) {
setOverviewSelectEnabled(false);
}
+
+ if (finalState.overviewUi() && finalState != MODAL_TASK) {
+ setTaskBorderEnabled(true);
+ }
+
if (finalState != OVERVIEW_SPLIT_SELECT) {
if (FeatureFlags.enableSplitContextually()) {
mSplitSelectStateController.resetState();
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index b542909..5b5d0de 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -145,6 +145,12 @@
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
}
+
+ // Set border after select mode changes to avoid showing border during state transition
+ if (!toState.overviewUi || toState == OVERVIEW_MODAL_TASK) {
+ setTaskBorderEnabled(false);
+ }
+
setFreezeViewVisibility(true);
if (mActivity.getDesktopVisibilityController() != null) {
mActivity.getDesktopVisibilityController().onLauncherStateChanged(toState);
@@ -165,6 +171,10 @@
setOverviewSelectEnabled(false);
}
+ if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) {
+ setTaskBorderEnabled(true);
+ }
+
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f55dce9..22a5064 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1410,6 +1410,17 @@
}
/**
+ * Enable or disable showing border on hover and focus change on task views
+ */
+ public void setTaskBorderEnabled(boolean enabled) {
+ int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = requireTaskViewAt(i);
+ taskView.setBorderEnabled(enabled);
+ }
+ }
+
+ /**
* Whether the Clear All button is hidden or fully visible. Used to determine if center
* displayed page is a task or the Clear All button.
*
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 49eda8f..af4f402 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -408,6 +408,7 @@
new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
+ private boolean mBorderEnabled;
// The current background requests to load the task thumbnail and icon
@Nullable
@@ -439,8 +440,15 @@
this(context, attrs, defStyleAttr, 0);
}
- public TaskView(
- Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ this(context, attrs, defStyleAttr, defStyleRes, null, null);
+ }
+
+ @VisibleForTesting
+ public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes, BorderAnimator focusBorderAnimator,
+ BorderAnimator hoverBorderAnimator) {
super(context, attrs, defStyleAttr, defStyleRes);
mActivity = StatefulActivity.fromContext(context);
setOnClickListener(this::onClick);
@@ -457,28 +465,35 @@
TypedArray styledAttrs = context.obtainStyledAttributes(
attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
- mFocusBorderAnimator = keyboardFocusHighlightEnabled
- ? BorderAnimator.createSimpleBorderAnimator(
- /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
- /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
- R.dimen.keyboard_quick_switch_border_width),
- /* boundsBuilder= */ this::updateBorderBounds,
- /* targetView= */ this,
- /* borderColor= */ styledAttrs.getColor(
- R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
- : null;
+ if (focusBorderAnimator != null) {
+ mFocusBorderAnimator = focusBorderAnimator;
+ } else {
+ mFocusBorderAnimator = keyboardFocusHighlightEnabled
+ ? BorderAnimator.createSimpleBorderAnimator(
+ /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
+ /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_width),
+ /* boundsBuilder= */ this::updateBorderBounds,
+ /* targetView= */ this,
+ /* borderColor= */ styledAttrs.getColor(
+ R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR))
+ : null;
+ }
- mHoverBorderAnimator = cursorHoverStatesEnabled
- ? BorderAnimator.createSimpleBorderAnimator(
- /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
- /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
- R.dimen.task_hover_border_width),
- /* boundsBuilder= */ this::updateBorderBounds,
- /* targetView= */ this,
- /* borderColor= */ styledAttrs.getColor(
- R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
- : null;
-
+ if (hoverBorderAnimator != null) {
+ mHoverBorderAnimator = hoverBorderAnimator;
+ } else {
+ mHoverBorderAnimator = cursorHoverStatesEnabled
+ ? BorderAnimator.createSimpleBorderAnimator(
+ /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
+ /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
+ R.dimen.task_hover_border_width),
+ /* boundsBuilder= */ this::updateBorderBounds,
+ /* targetView= */ this,
+ /* borderColor= */ styledAttrs.getColor(
+ R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
+ : null;
+ }
styledAttrs.recycle();
}
@@ -538,24 +553,25 @@
}
@Override
- protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+ public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- if (mFocusBorderAnimator != null) {
+ if (mFocusBorderAnimator != null && mBorderEnabled) {
mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
}
}
@Override
public boolean onHoverEvent(MotionEvent event) {
- if (mHoverBorderAnimator != null) {
+ if (mHoverBorderAnimator != null && mBorderEnabled) {
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_ENTER:
- mHoverBorderAnimator.setBorderVisibility(
- /* visible= */ true, /* animated= */ true);
+ mHoverBorderAnimator.setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
break;
case MotionEvent.ACTION_HOVER_EXIT:
- mHoverBorderAnimator.setBorderVisibility(
- /* visible= */ false, /* animated= */ true);
+ mHoverBorderAnimator.setBorderVisibility(/* visible= */ false, /* animated= */
+ true);
break;
default:
break;
@@ -564,6 +580,24 @@
return super.onHoverEvent(event);
}
+ /**
+ * Enable or disable showing border on hover and focus change
+ */
+ public void setBorderEnabled(boolean enabled) {
+ mBorderEnabled = enabled;
+ // Set the animation correctly in case it misses the hover/focus event during state
+ // transition
+ if (mHoverBorderAnimator != null) {
+ mHoverBorderAnimator.setBorderVisibility(/* visible= */
+ enabled && isHovered(), /* animated= */ true);
+ }
+
+ if (mFocusBorderAnimator != null) {
+ mFocusBorderAnimator.setBorderVisibility(/* visible= */
+ enabled && isFocused(), /* animated= */true);
+ }
+ }
+
@Override
public boolean onInterceptHoverEvent(MotionEvent event) {
if (enableCursorHoverStates()) {
@@ -1327,6 +1361,7 @@
mSnapshotView.setThumbnail(mTask, null);
setOverlayEnabled(false);
onTaskListVisibilityChanged(false);
+ mBorderEnabled = false;
}
public float getTaskCornerRadius() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaskViewTest.java b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
new file mode 100644
index 0000000..d744194
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaskViewTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.quickstep;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.util.BorderAnimator;
+import com.android.quickstep.views.TaskView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class TaskViewTest {
+
+ @Mock
+ private StatefulActivity mContext;
+ @Mock
+ private Resources mResource;
+ @Mock
+ private BorderAnimator mHoverAnimator;
+ @Mock
+ private BorderAnimator mFocusAnimator;
+ private TaskView mTaskView;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mResource.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
+ when(mResource.getConfiguration()).thenReturn(new Configuration());
+
+ when(mContext.getResources()).thenReturn(mResource);
+ when(mContext.getTheme()).thenReturn(mock(Resources.Theme.class));
+ when(mContext.getApplicationInfo()).thenReturn(mock(ApplicationInfo.class));
+ when(mContext.obtainStyledAttributes(any(), any(), anyInt(), anyInt())).thenReturn(
+ mock(TypedArray.class));
+
+ mTaskView = new TaskView(mContext, null, 0, 0, mFocusAnimator, mHoverAnimator);
+ }
+
+ @Test
+ public void notShowBorderOnBorderDisabled() {
+ mTaskView.setBorderEnabled(/* enabled= */ false);
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+ mTaskView.onHoverEvent(MotionEvent.obtain(event));
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+
+ mTaskView.onFocusChanged(false, 0, new Rect());
+ verify(mFocusAnimator, never()).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+ }
+
+ @Test
+ public void showBorderOnBorderEnabled() {
+ mTaskView.setBorderEnabled(/* enabled= */ true);
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+ mTaskView.onHoverEvent(MotionEvent.obtain(event));
+ verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+ mTaskView.onFocusChanged(true, 0, new Rect());
+ verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ true, /* animated= */
+ true);
+ }
+
+ @Test
+ public void hideBorderOnBorderDisabled() {
+ mTaskView.setBorderEnabled(/* enabled= */ false);
+ verify(mHoverAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */
+ true);
+ verify(mFocusAnimator, times(1)).setBorderVisibility(/* visible= */ false, /* animated= */
+ true);
+ }
+
+ @Test
+ public void notShowBorderByDefault() {
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0.0f, 0.0f, 0);
+ mTaskView.onHoverEvent(MotionEvent.obtain(event));
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
+ true);
+ mTaskView.onFocusChanged(true, 0, new Rect());
+ verify(mHoverAnimator, never()).setBorderVisibility(/* visible= */ false, /* animated= */
+ true);
+ }
+}