Loading quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +11 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,12 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta 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); } Loading @@ -253,6 +259,11 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta if (finalState != MODAL_TASK) { setOverviewSelectEnabled(false); } if (finalState.overviewUi() && finalState != MODAL_TASK) { setTaskBorderEnabled(true); } if (finalState != OVERVIEW_SPLIT_SELECT) { if (FeatureFlags.enableSplitContextually()) { mSplitSelectStateController.resetState(); Loading quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +10 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,12 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher 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); Loading @@ -165,6 +171,10 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher setOverviewSelectEnabled(false); } if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) { setTaskBorderEnabled(true); } if (isOverlayEnabled) { runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)); Loading quickstep/src/com/android/quickstep/views/RecentsView.java +11 −0 Original line number Diff line number Diff line Loading @@ -1409,6 +1409,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T updateLocusId(); } /** * 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. Loading quickstep/src/com/android/quickstep/views/TaskView.java +66 −31 Original line number Diff line number Diff line Loading @@ -408,6 +408,7 @@ public class TaskView extends FrameLayout implements Reusable { new TaskIdAttributeContainer[2]; private boolean mShowScreenshot; private boolean mBorderEnabled; // The current background requests to load the task thumbnail and icon @Nullable Loading Loading @@ -439,8 +440,15 @@ public class TaskView extends FrameLayout implements Reusable { 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); Loading @@ -457,6 +465,9 @@ public class TaskView extends FrameLayout implements Reusable { TypedArray styledAttrs = context.obtainStyledAttributes( attrs, R.styleable.TaskView, defStyleAttr, defStyleRes); if (focusBorderAnimator != null) { mFocusBorderAnimator = focusBorderAnimator; } else { mFocusBorderAnimator = keyboardFocusHighlightEnabled ? BorderAnimator.createSimpleBorderAnimator( /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, Loading @@ -467,7 +478,11 @@ public class TaskView extends FrameLayout implements Reusable { /* borderColor= */ styledAttrs.getColor( R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR)) : null; } if (hoverBorderAnimator != null) { mHoverBorderAnimator = hoverBorderAnimator; } else { mHoverBorderAnimator = cursorHoverStatesEnabled ? BorderAnimator.createSimpleBorderAnimator( /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, Loading @@ -478,7 +493,7 @@ public class TaskView extends FrameLayout implements Reusable { /* borderColor= */ styledAttrs.getColor( R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR)) : null; } styledAttrs.recycle(); } Loading Loading @@ -538,24 +553,25 @@ public class TaskView extends FrameLayout implements Reusable { } @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; Loading @@ -564,6 +580,24 @@ public class TaskView extends FrameLayout implements Reusable { 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()) { Loading Loading @@ -1327,6 +1361,7 @@ public class TaskView extends FrameLayout implements Reusable { mSnapshotView.setThumbnail(mTask, null); setOverlayEnabled(false); onTaskListVisibilityChanged(false); mBorderEnabled = false; } public float getTaskCornerRadius() { Loading quickstep/tests/src/com/android/quickstep/TaskViewTest.java 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * 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); } } Loading
quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +11 −0 Original line number Diff line number Diff line Loading @@ -238,6 +238,12 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta 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); } Loading @@ -253,6 +259,11 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta if (finalState != MODAL_TASK) { setOverviewSelectEnabled(false); } if (finalState.overviewUi() && finalState != MODAL_TASK) { setTaskBorderEnabled(true); } if (finalState != OVERVIEW_SPLIT_SELECT) { if (FeatureFlags.enableSplitContextually()) { mSplitSelectStateController.resetState(); Loading
quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +10 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,12 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher 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); Loading @@ -165,6 +171,10 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher setOverviewSelectEnabled(false); } if (finalState.overviewUi && finalState != OVERVIEW_MODAL_TASK) { setTaskBorderEnabled(true); } if (isOverlayEnabled) { runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true)); Loading
quickstep/src/com/android/quickstep/views/RecentsView.java +11 −0 Original line number Diff line number Diff line Loading @@ -1409,6 +1409,17 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T updateLocusId(); } /** * 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. Loading
quickstep/src/com/android/quickstep/views/TaskView.java +66 −31 Original line number Diff line number Diff line Loading @@ -408,6 +408,7 @@ public class TaskView extends FrameLayout implements Reusable { new TaskIdAttributeContainer[2]; private boolean mShowScreenshot; private boolean mBorderEnabled; // The current background requests to load the task thumbnail and icon @Nullable Loading Loading @@ -439,8 +440,15 @@ public class TaskView extends FrameLayout implements Reusable { 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); Loading @@ -457,6 +465,9 @@ public class TaskView extends FrameLayout implements Reusable { TypedArray styledAttrs = context.obtainStyledAttributes( attrs, R.styleable.TaskView, defStyleAttr, defStyleRes); if (focusBorderAnimator != null) { mFocusBorderAnimator = focusBorderAnimator; } else { mFocusBorderAnimator = keyboardFocusHighlightEnabled ? BorderAnimator.createSimpleBorderAnimator( /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, Loading @@ -467,7 +478,11 @@ public class TaskView extends FrameLayout implements Reusable { /* borderColor= */ styledAttrs.getColor( R.styleable.TaskView_focusBorderColor, DEFAULT_BORDER_COLOR)) : null; } if (hoverBorderAnimator != null) { mHoverBorderAnimator = hoverBorderAnimator; } else { mHoverBorderAnimator = cursorHoverStatesEnabled ? BorderAnimator.createSimpleBorderAnimator( /* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius, Loading @@ -478,7 +493,7 @@ public class TaskView extends FrameLayout implements Reusable { /* borderColor= */ styledAttrs.getColor( R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR)) : null; } styledAttrs.recycle(); } Loading Loading @@ -538,24 +553,25 @@ public class TaskView extends FrameLayout implements Reusable { } @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; Loading @@ -564,6 +580,24 @@ public class TaskView extends FrameLayout implements Reusable { 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()) { Loading Loading @@ -1327,6 +1361,7 @@ public class TaskView extends FrameLayout implements Reusable { mSnapshotView.setThumbnail(mTask, null); setOverlayEnabled(false); onTaskListVisibilityChanged(false); mBorderEnabled = false; } public float getTaskCornerRadius() { Loading
quickstep/tests/src/com/android/quickstep/TaskViewTest.java 0 → 100644 +118 −0 Original line number Diff line number Diff line /* * 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); } }