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

Commit b3f069e0 authored by Vinit Nayak's avatar Vinit Nayak Committed by Automerger Merge Worker
Browse files

Merge "Add task unpinning support for 3 button taskbar" into sc-v2-dev am: f198f6e4

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/16339859

Change-Id: Id2c29bdcbc43f6e2f556a2b43f882c4dc24ec3e9
parents e9dcc967 f198f6e4
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;

import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
@@ -86,6 +87,7 @@ public class NavbarButtonsViewController {
    private static final int FLAG_DISABLE_RECENTS = 1 << 8;
    private static final int FLAG_DISABLE_BACK = 1 << 9;
    private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
    private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 10;

    private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;

@@ -152,7 +154,9 @@ public class NavbarButtonsViewController {
        mPropertyHolders.add(new StatePropertyHolder(
                mControllers.taskbarViewController.getTaskbarIconAlpha()
                        .getProperty(ALPHA_INDEX_KEYGUARD),
                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
                flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
                        && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0,
                MultiValueAlpha.VALUE, 1, 0));

        mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
                .getKeyguardBgTaskbar(),
@@ -286,6 +290,7 @@ public class NavbarButtonsViewController {
        int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
        boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
        boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;

        // TODO(b/202218289) we're getting IME as not visible on lockscreen from system
        updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -295,6 +300,7 @@ public class NavbarButtonsViewController {
        updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
        updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
        updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
        updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);

        if (mA11yButton != null) {
            // Only used in 3 button
+1 −0
Original line number Diff line number Diff line
@@ -361,6 +361,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
        mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
        mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
                fromInit);
        mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
    }

    /**
+3 −2
Original line number Diff line number Diff line
@@ -29,8 +29,8 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;

import androidx.annotation.NonNull;
@@ -93,7 +93,8 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen
        Display display =
                service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
        mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
        mNavButtonController = new TaskbarNavButtonController(service);
        mNavButtonController = new TaskbarNavButtonController(service,
                SystemUiProxy.INSTANCE.get(mContext), new Handler());
        mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
        mComponentCallbacks = new ComponentCallbacks() {
            private Configuration mOldConfig = mContext.getResources().getConfiguration();
+84 −13
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.launcher3.taskbar;

import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;

import android.os.Bundle;
import android.os.Handler;

import androidx.annotation.IntDef;

@@ -40,6 +42,13 @@ import java.lang.annotation.RetentionPolicy;
 */
public class TaskbarNavButtonController {

    /** Allow some time in between the long press for back and recents. */
    static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
    static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;

    private long mLastScreenPinLongPress;
    private boolean mScreenPinned;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {
            BUTTON_BACK,
@@ -57,10 +66,20 @@ public class TaskbarNavButtonController {
    static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
    static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;

    private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
    private int mLongPressedButtons = 0;

    private final TouchInteractionService mService;
    private final SystemUiProxy mSystemUiProxy;
    private final Handler mHandler;

    private final Runnable mResetLongPress = this::resetScreenUnpin;

    public TaskbarNavButtonController(TouchInteractionService service) {
    public TaskbarNavButtonController(TouchInteractionService service,
            SystemUiProxy systemUiProxy, Handler handler) {
        mService = service;
        mSystemUiProxy = systemUiProxy;
        mHandler = handler;
    }

    public void onButtonClick(@TaskbarButton int buttonType) {
@@ -72,13 +91,13 @@ public class TaskbarNavButtonController {
                navigateHome();
                break;
            case BUTTON_RECENTS:
                navigateToOverview();;
                navigateToOverview();
                break;
            case BUTTON_IME_SWITCH:
                showIMESwitcher();
                break;
            case BUTTON_A11Y:
                notifyImeClick(false /* longClick */);
                notifyA11yClick(false /* longClick */);
                break;
        }
    }
@@ -89,46 +108,98 @@ public class TaskbarNavButtonController {
                startAssistant();
                return true;
            case BUTTON_A11Y:
                notifyImeClick(true /* longClick */);
                notifyA11yClick(true /* longClick */);
                return true;
            case BUTTON_BACK:
            case BUTTON_IME_SWITCH:
            case BUTTON_RECENTS:
                mLongPressedButtons |= buttonType;
                return determineScreenUnpin();
            case BUTTON_IME_SWITCH:
            default:
                return false;
        }
    }

    /**
     * Checks if the user has long pressed back and recents buttons
     * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
     * If so, then requests the system to turn off screen pinning.
     *
     * @return true if the long press is a valid user action in attempting to unpin an app
     *         Will always return {@code false} when screen pinning is not active.
     *         NOTE: Returning true does not mean that screen pinning has stopped
     */
    private boolean determineScreenUnpin() {
        long timeNow = System.currentTimeMillis();
        if (!mScreenPinned) {
            return false;
        }

        if (mLastScreenPinLongPress == 0) {
            // First button long press registered, just mark time and wait for second button press
            mLastScreenPinLongPress = System.currentTimeMillis();
            mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
            return true;
        }

        if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
            // Too long in-between presses, reset the clock
            resetScreenUnpin();
            return false;
        }

        if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
            // Hooray! They did it (finally...)
            mSystemUiProxy.stopScreenPinning();
            mHandler.removeCallbacks(mResetLongPress);
            resetScreenUnpin();
        }
        return true;
    }

    private void resetScreenUnpin() {
        mLongPressedButtons = 0;
        mLastScreenPinLongPress = 0;
    }

    public void updateSysuiFlags(int sysuiFlags) {
        mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
    }

    private void navigateHome() {
        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
    }

    private void navigateToOverview() {
        if (mScreenPinned) {
            return;
        }
        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
    }

    private void executeBack() {
        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
        mSystemUiProxy.onBackPressed();
    }

    private void showIMESwitcher() {
        SystemUiProxy.INSTANCE.getNoCreate().onImeSwitcherPressed();
        mSystemUiProxy.onImeSwitcherPressed();
    }

    private void notifyImeClick(boolean longClick) {
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
    private void notifyA11yClick(boolean longClick) {
        if (longClick) {
            systemUiProxy.notifyAccessibilityButtonLongClicked();
            mSystemUiProxy.notifyAccessibilityButtonLongClicked();
        } else {
            systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
            mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
        }
    }

    private void startAssistant() {
        if (mScreenPinned) {
            return;
        }
        Bundle args = new Bundle();
        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
        systemUiProxy.startAssistant(args);
        mSystemUiProxy.startAssistant(args);
    }
}
+159 −0
Original line number Diff line number Diff line
package com.android.launcher3.taskbar;

import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.os.Handler;

import androidx.test.runner.AndroidJUnit4;

import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@RunWith(AndroidJUnit4.class)
public class TaskbarNavButtonControllerTest {

    private final static int DISPLAY_ID = 2;

    @Mock
    SystemUiProxy mockSystemUiProxy;
    @Mock
    TouchInteractionService mockService;
    @Mock
    OverviewCommandHelper mockCommandHelper;
    @Mock
    Handler mockHandler;

    private TaskbarNavButtonController mNavButtonController;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
        when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
        mNavButtonController = new TaskbarNavButtonController(mockService,
                mockSystemUiProxy, mockHandler);
    }

    @Test
    public void testPressBack() {
        mNavButtonController.onButtonClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(1)).onBackPressed();
    }

    @Test
    public void testPressImeSwitcher() {
        mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
        verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
    }

    @Test
    public void testPressA11yShortClick() {
        mNavButtonController.onButtonClick(BUTTON_A11Y);
        verify(mockSystemUiProxy, times(1))
                .notifyAccessibilityButtonClicked(DISPLAY_ID);
    }

    @Test
    public void testPressA11yLongClick() {
        mNavButtonController.onButtonLongClick(BUTTON_A11Y);
        verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
    }

    @Test
    public void testLongPressHome() {
        mNavButtonController.onButtonLongClick(BUTTON_HOME);
        verify(mockSystemUiProxy, times(1)).startAssistant(any());
    }

    @Test
    public void testPressHome() {
        mNavButtonController.onButtonClick(BUTTON_HOME);
        verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
    }

    @Test
    public void testPressRecents() {
        mNavButtonController.onButtonClick(BUTTON_RECENTS);
        verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
    }

    @Test
    public void testPressRecentsWithScreenPinned() {
        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
        mNavButtonController.onButtonClick(BUTTON_RECENTS);
        verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
    }

    @Test
    public void testLongPressBackRecentsNotPinned() {
        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
        mNavButtonController.onButtonLongClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
    }

    @Test
    public void testLongPressBackRecentsPinned() {
        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
        mNavButtonController.onButtonLongClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
    }

    @Test
    public void testLongPressBackRecentsTooLongPinned() {
        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
        try {
            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mNavButtonController.onButtonLongClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
    }

    @Test
    public void testLongPressBackRecentsMultipleAttemptPinned() {
        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
        try {
            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mNavButtonController.onButtonLongClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(0)).stopScreenPinning();

        // Try again w/in threshold
        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
        mNavButtonController.onButtonLongClick(BUTTON_BACK);
        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
    }

    @Test
    public void testLongPressHomeScreenPinned() {
        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
        mNavButtonController.onButtonLongClick(BUTTON_HOME);
        verify(mockSystemUiProxy, times(0)).startAssistant(any());
    }
}