• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.taskbar;
18 
19 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
20 import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS;
22 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_TAP;
23 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS;
24 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_BACK_BUTTON_TAP;
25 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS;
26 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_HOME_BUTTON_TAP;
27 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
28 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
29 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
30 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
31 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
33 
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.util.Log;
37 import android.view.HapticFeedbackConstants;
38 import android.view.View;
39 
40 import androidx.annotation.IntDef;
41 import androidx.annotation.Nullable;
42 import androidx.annotation.StringRes;
43 
44 import com.android.launcher3.R;
45 import com.android.launcher3.logging.StatsLogManager;
46 import com.android.launcher3.testing.TestLogging;
47 import com.android.launcher3.testing.shared.TestProtocol;
48 import com.android.quickstep.OverviewCommandHelper;
49 import com.android.quickstep.SystemUiProxy;
50 import com.android.quickstep.TaskUtils;
51 import com.android.quickstep.TouchInteractionService;
52 
53 import java.io.PrintWriter;
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 
57 /**
58  * Controller for 3 button mode in the taskbar.
59  * Handles all the functionality of the various buttons, making/routing the right calls into
60  * launcher or sysui/system.
61  */
62 public class TaskbarNavButtonController implements TaskbarControllers.LoggableTaskbarController {
63 
64     /** Allow some time in between the long press for back and recents. */
65     static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
66     static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
67     private static final String TAG = TaskbarNavButtonController.class.getSimpleName();
68 
69     private long mLastScreenPinLongPress;
70     private boolean mScreenPinned;
71     private boolean mAssistantLongPressEnabled;
72 
73     @Override
dumpLogs(String prefix, PrintWriter pw)74     public void dumpLogs(String prefix, PrintWriter pw) {
75         pw.println(prefix + "TaskbarNavButtonController:");
76 
77         pw.println(prefix + "\tmLastScreenPinLongPress=" + mLastScreenPinLongPress);
78         pw.println(prefix + "\tmScreenPinned=" + mScreenPinned);
79     }
80 
81     @Retention(RetentionPolicy.SOURCE)
82     @IntDef(value = {
83             BUTTON_BACK,
84             BUTTON_HOME,
85             BUTTON_RECENTS,
86             BUTTON_IME_SWITCH,
87             BUTTON_A11Y,
88             BUTTON_QUICK_SETTINGS,
89             BUTTON_NOTIFICATIONS,
90     })
91 
92     public @interface TaskbarButton {}
93 
94     static final int BUTTON_BACK = 1;
95     static final int BUTTON_HOME = BUTTON_BACK << 1;
96     static final int BUTTON_RECENTS = BUTTON_HOME << 1;
97     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
98     static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
99     static final int BUTTON_QUICK_SETTINGS = BUTTON_A11Y << 1;
100     static final int BUTTON_NOTIFICATIONS = BUTTON_QUICK_SETTINGS << 1;
101 
102     private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
103     private int mLongPressedButtons = 0;
104 
105     private final TouchInteractionService mService;
106     private final SystemUiProxy mSystemUiProxy;
107     private final Handler mHandler;
108     @Nullable private StatsLogManager mStatsLogManager;
109 
110     private final Runnable mResetLongPress = this::resetScreenUnpin;
111 
TaskbarNavButtonController(TouchInteractionService service, SystemUiProxy systemUiProxy, Handler handler)112     public TaskbarNavButtonController(TouchInteractionService service,
113             SystemUiProxy systemUiProxy, Handler handler) {
114         mService = service;
115         mSystemUiProxy = systemUiProxy;
116         mHandler = handler;
117     }
118 
onButtonClick(@askbarButton int buttonType, View view)119     public void onButtonClick(@TaskbarButton int buttonType, View view) {
120         // Provide the same haptic feedback that the system offers for virtual keys.
121         view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
122         switch (buttonType) {
123             case BUTTON_BACK:
124                 logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
125                 executeBack();
126                 break;
127             case BUTTON_HOME:
128                 logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
129                 navigateHome();
130                 break;
131             case BUTTON_RECENTS:
132                 logEvent(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP);
133                 navigateToOverview();
134                 break;
135             case BUTTON_IME_SWITCH:
136                 logEvent(LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP);
137                 showIMESwitcher();
138                 break;
139             case BUTTON_A11Y:
140                 logEvent(LAUNCHER_TASKBAR_A11Y_BUTTON_TAP);
141                 notifyA11yClick(false /* longClick */);
142                 break;
143             case BUTTON_QUICK_SETTINGS:
144                 showQuickSettings();
145                 break;
146             case BUTTON_NOTIFICATIONS:
147                 showNotifications();
148                 break;
149         }
150     }
151 
onButtonLongClick(@askbarButton int buttonType, View view)152     public boolean onButtonLongClick(@TaskbarButton int buttonType, View view) {
153         // Provide the same haptic feedback that the system offers for virtual keys.
154         view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
155         switch (buttonType) {
156             case BUTTON_HOME:
157                 logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
158                 startAssistant();
159                 return true;
160             case BUTTON_A11Y:
161                 logEvent(LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS);
162                 notifyA11yClick(true /* longClick */);
163                 return true;
164             case BUTTON_BACK:
165                 logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS);
166                 return backRecentsLongpress(buttonType);
167             case BUTTON_RECENTS:
168                 logEvent(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS);
169                 return backRecentsLongpress(buttonType);
170             case BUTTON_IME_SWITCH:
171             default:
172                 return false;
173         }
174     }
175 
getButtonContentDescription(@askbarButton int buttonType)176     public @StringRes int getButtonContentDescription(@TaskbarButton int buttonType) {
177         switch (buttonType) {
178             case BUTTON_HOME:
179                 return R.string.taskbar_button_home;
180             case BUTTON_A11Y:
181                 return R.string.taskbar_button_a11y;
182             case BUTTON_BACK:
183                 return R.string.taskbar_button_back;
184             case BUTTON_IME_SWITCH:
185                 return R.string.taskbar_button_ime_switcher;
186             case BUTTON_RECENTS:
187                 return R.string.taskbar_button_recents;
188             case BUTTON_NOTIFICATIONS:
189                 return R.string.taskbar_button_notifications;
190             case BUTTON_QUICK_SETTINGS:
191                 return R.string.taskbar_button_quick_settings;
192             default:
193                 return 0;
194         }
195     }
196 
backRecentsLongpress(@askbarButton int buttonType)197     private boolean backRecentsLongpress(@TaskbarButton int buttonType) {
198         mLongPressedButtons |= buttonType;
199         return determineScreenUnpin();
200     }
201 
202     /**
203      * Checks if the user has long pressed back and recents buttons
204      * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
205      * If so, then requests the system to turn off screen pinning.
206      *
207      * @return true if the long press is a valid user action in attempting to unpin an app
208      *         Will always return {@code false} when screen pinning is not active.
209      *         NOTE: Returning true does not mean that screen pinning has stopped
210      */
determineScreenUnpin()211     private boolean determineScreenUnpin() {
212         long timeNow = System.currentTimeMillis();
213         if (!mScreenPinned) {
214             return false;
215         }
216 
217         if (mLastScreenPinLongPress == 0) {
218             // First button long press registered, just mark time and wait for second button press
219             mLastScreenPinLongPress = System.currentTimeMillis();
220             mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
221             return true;
222         }
223 
224         if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
225             // Too long in-between presses, reset the clock
226             resetScreenUnpin();
227             return false;
228         }
229 
230         if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
231             // Hooray! They did it (finally...)
232             mSystemUiProxy.stopScreenPinning();
233             mHandler.removeCallbacks(mResetLongPress);
234             resetScreenUnpin();
235         }
236         return true;
237     }
238 
resetScreenUnpin()239     private void resetScreenUnpin() {
240         mLongPressedButtons = 0;
241         mLastScreenPinLongPress = 0;
242     }
243 
updateSysuiFlags(int sysuiFlags)244     public void updateSysuiFlags(int sysuiFlags) {
245         mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
246     }
247 
init(TaskbarControllers taskbarControllers)248     public void init(TaskbarControllers taskbarControllers) {
249         mStatsLogManager = taskbarControllers.getTaskbarActivityContext().getStatsLogManager();
250     }
251 
onDestroy()252     public void onDestroy() {
253         mStatsLogManager = null;
254     }
255 
setAssistantLongPressEnabled(boolean assistantLongPressEnabled)256     public void setAssistantLongPressEnabled(boolean assistantLongPressEnabled) {
257         mAssistantLongPressEnabled = assistantLongPressEnabled;
258     }
259 
logEvent(StatsLogManager.LauncherEvent event)260     private void logEvent(StatsLogManager.LauncherEvent event) {
261         if (mStatsLogManager == null) {
262             Log.w(TAG, "No stats log manager to log taskbar button event");
263             return;
264         }
265         mStatsLogManager.logger().log(event);
266     }
267 
navigateHome()268     private void navigateHome() {
269         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
270         mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
271     }
272 
navigateToOverview()273     private void navigateToOverview() {
274         if (mScreenPinned) {
275             return;
276         }
277         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
278         TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
279         mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
280     }
281 
executeBack()282     private void executeBack() {
283         mSystemUiProxy.onBackPressed();
284     }
285 
showIMESwitcher()286     private void showIMESwitcher() {
287         mSystemUiProxy.onImeSwitcherPressed();
288     }
289 
notifyA11yClick(boolean longClick)290     private void notifyA11yClick(boolean longClick) {
291         if (longClick) {
292             mSystemUiProxy.notifyAccessibilityButtonLongClicked();
293         } else {
294             mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
295         }
296     }
297 
startAssistant()298     private void startAssistant() {
299         if (mScreenPinned || !mAssistantLongPressEnabled) {
300             return;
301         }
302         Bundle args = new Bundle();
303         args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
304         mSystemUiProxy.startAssistant(args);
305     }
306 
showQuickSettings()307     private void showQuickSettings() {
308         mSystemUiProxy.toggleNotificationPanel();
309     }
310 
showNotifications()311     private void showNotifications() {
312         mSystemUiProxy.toggleNotificationPanel();
313     }
314 }
315