| /* |
| * Copyright 2021 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 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; |
| |
| import com.android.launcher3.testing.TestLogging; |
| import com.android.launcher3.testing.TestProtocol; |
| import com.android.quickstep.OverviewCommandHelper; |
| import com.android.quickstep.SystemUiProxy; |
| import com.android.quickstep.TouchInteractionService; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| /** |
| * Controller for 3 button mode in the taskbar. |
| * Handles all the functionality of the various buttons, making/routing the right calls into |
| * launcher or sysui/system. |
| */ |
| 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, |
| BUTTON_HOME, |
| BUTTON_RECENTS, |
| BUTTON_IME_SWITCH, |
| BUTTON_A11Y, |
| }) |
| |
| public @interface TaskbarButton {} |
| |
| static final int BUTTON_BACK = 1; |
| static final int BUTTON_HOME = BUTTON_BACK << 1; |
| static final int BUTTON_RECENTS = BUTTON_HOME << 1; |
| 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, |
| SystemUiProxy systemUiProxy, Handler handler) { |
| mService = service; |
| mSystemUiProxy = systemUiProxy; |
| mHandler = handler; |
| } |
| |
| public void onButtonClick(@TaskbarButton int buttonType) { |
| switch (buttonType) { |
| case BUTTON_BACK: |
| executeBack(); |
| break; |
| case BUTTON_HOME: |
| navigateHome(); |
| break; |
| case BUTTON_RECENTS: |
| navigateToOverview(); |
| break; |
| case BUTTON_IME_SWITCH: |
| showIMESwitcher(); |
| break; |
| case BUTTON_A11Y: |
| notifyA11yClick(false /* longClick */); |
| break; |
| } |
| } |
| |
| public boolean onButtonLongClick(@TaskbarButton int buttonType) { |
| switch (buttonType) { |
| case BUTTON_HOME: |
| startAssistant(); |
| return true; |
| case BUTTON_A11Y: |
| notifyA11yClick(true /* longClick */); |
| return true; |
| case BUTTON_BACK: |
| 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() { |
| mSystemUiProxy.onBackPressed(); |
| } |
| |
| private void showIMESwitcher() { |
| mSystemUiProxy.onImeSwitcherPressed(); |
| } |
| |
| private void notifyA11yClick(boolean longClick) { |
| if (longClick) { |
| mSystemUiProxy.notifyAccessibilityButtonLongClicked(); |
| } else { |
| 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); |
| mSystemUiProxy.startAssistant(args); |
| } |
| } |