Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4cda2ad authored by helencheuk's avatar helencheuk
Browse files

Not show hover border on select mode

Fix: 295175854
Test: TaskViewTest
Flag: ACONFIG com.android.launcher3.enable_cursor_hover_states Nextfood
Change-Id: I81b1af27b590ea509e8ec84a9d3ff008f31da21f
parent 646c95e6
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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);
    }

@@ -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();
+10 −0
Original line number Diff line number Diff line
@@ -144,6 +144,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);
@@ -164,6 +170,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));
+11 −0
Original line number Diff line number Diff line
@@ -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.
+66 −31
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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,
@@ -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,
@@ -478,7 +493,7 @@ public class TaskView extends FrameLayout implements Reusable {
                    /* borderColor= */ styledAttrs.getColor(
                            R.styleable.TaskView_hoverBorderColor, DEFAULT_BORDER_COLOR))
                    : null;

        }
        styledAttrs.recycle();
    }

@@ -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;
@@ -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()) {
@@ -1327,6 +1361,7 @@ public class TaskView extends FrameLayout implements Reusable {
        mSnapshotView.setThumbnail(mTask, null);
        setOverlayEnabled(false);
        onTaskListVisibilityChanged(false);
        mBorderEnabled = false;
    }

    public float getTaskCornerRadius() {
+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);
    }
}