• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
20 
21 import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
22 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS;
23 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
24 import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TASKBAR_OVERFLOW;
25 
26 import android.annotation.SuppressLint;
27 import android.content.Context;
28 import android.view.GestureDetector;
29 import android.view.HapticFeedbackConstants;
30 import android.view.InputDevice;
31 import android.view.MotionEvent;
32 import android.view.View;
33 
34 import androidx.annotation.NonNull;
35 import androidx.annotation.Nullable;
36 
37 import com.android.internal.jank.Cuj;
38 import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
39 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
40 import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
41 
42 /**
43  * Callbacks for {@link TaskbarView} to interact with its controller.
44  */
45 public class TaskbarViewCallbacks {
46 
47     private final TaskbarActivityContext mActivity;
48     private final TaskbarControllers mControllers;
49     private final TaskbarView mTaskbarView;
50     private final GestureDetector mGestureDetector;
51 
TaskbarViewCallbacks(TaskbarActivityContext activity, TaskbarControllers controllers, TaskbarView taskbarView)52     public TaskbarViewCallbacks(TaskbarActivityContext activity, TaskbarControllers controllers,
53             TaskbarView taskbarView) {
54         mActivity = activity;
55         mControllers = controllers;
56         mTaskbarView = taskbarView;
57         mGestureDetector = new GestureDetector(activity, new TaskbarViewGestureListener());
58     }
59 
getIconOnClickListener()60     public View.OnClickListener getIconOnClickListener() {
61         return mActivity.getItemOnClickListener();
62     }
63 
64     /** Trigger All Apps button click action. */
triggerAllAppsButtonClick(View v)65     public void triggerAllAppsButtonClick(View v) {
66         InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
67                 /* tag= */ "TASKBAR_BUTTON");
68         mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
69         if (mActivity.showLockedTaskbarOnHome()
70                 || mActivity.showDesktopTaskbarForFreeformDisplay()) {
71             // If the taskbar can be shown on the home screen, use mAllAppsToggler to toggle all
72             // apps, which will toggle the launcher activity all apps when on home screen.
73             // TODO(b/395913143): Reconsider this if a gap in taskbar all apps functionality that
74             //  prevents users to drag items to workspace is addressed.
75             mControllers.uiController.toggleAllApps(false);
76         } else {
77             mControllers.taskbarAllAppsController.toggle();
78         }
79     }
80 
81     /** Trigger All Apps button long click action. */
triggerAllAppsButtonLongClick()82     public void triggerAllAppsButtonLongClick() {
83         mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_LONG_PRESS);
84     }
85 
86     /** @return true if haptic feedback should occur when long pressing the all apps button. */
isAllAppsButtonHapticFeedbackEnabled(Context context)87     public boolean isAllAppsButtonHapticFeedbackEnabled(Context context) {
88         return false;
89     }
90 
91     @SuppressLint("ClickableViewAccessibility")
getTaskbarTouchListener()92     public View.OnTouchListener getTaskbarTouchListener() {
93         return (view, event) -> mGestureDetector.onTouchEvent(event);
94     }
95 
getTaskbarDividerLongClickListener()96     public View.OnLongClickListener getTaskbarDividerLongClickListener() {
97         return v -> {
98             mControllers.taskbarPinningController.showPinningView(v, getDividerCenterX());
99             return true;
100         };
101     }
102 
getTaskbarDividerRightClickListener()103     public View.OnTouchListener getTaskbarDividerRightClickListener() {
104         return (v, event) -> {
105             if (event.isFromSource(InputDevice.SOURCE_MOUSE)
106                     && event.getAction() == MotionEvent.ACTION_DOWN
107                     && event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
108                 mControllers.taskbarPinningController.showPinningView(v, getDividerCenterX());
109                 return true;
110             }
111             return false;
112         };
113     }
114 
115     public View.OnLongClickListener getIconOnLongClickListener() {
116         return mControllers.taskbarDragController::startDragOnLongClick;
117     }
118 
119     /** Gets the hover listener for the provided icon view. */
120     public View.OnHoverListener getIconOnHoverListener(View icon) {
121         return new TaskbarHoverToolTipController(mActivity, mTaskbarView, icon);
122     }
123 
124     /** Callback invoked before Taskbar icons are laid out. */
125     void onPreLayoutChildren() {
126         if (enableTaskbarPinning() && ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue()) {
127             mControllers.taskbarViewController.updateTaskbarIconTranslationXForPinning();
128         }
129     }
130 
131     /**
132      * Notifies launcher to update icon alignment.
133      */
134     public void notifyIconLayoutBoundsChanged() {
135         mControllers.uiController.onIconLayoutBoundsChanged();
136     }
137 
138     /**
139      * Notifies the taskbar scrim when the visibility of taskbar changes.
140      */
141     public void notifyVisibilityChanged() {
142         mControllers.taskbarScrimViewController.onTaskbarVisibilityChanged(
143                 mTaskbarView.getVisibility());
144     }
145 
146     /**
147      * Get current location of bubble bar, if it is visible.
148      * Returns {@code null} if bubble bar is not shown.
149      */
150     @Nullable
151     public BubbleBarLocation getBubbleBarLocationIfVisible() {
152         BubbleBarViewController bubbleBarViewController =
153                 mControllers.bubbleControllers.map(c -> c.bubbleBarViewController).orElse(null);
154         if (bubbleBarViewController != null && bubbleBarViewController.isBubbleBarVisible()) {
155             return bubbleBarViewController.getBubbleBarLocation();
156         }
157         return null;
158     }
159 
160     /**
161      * Get the max bubble bar collapsed width for the current bubble bar visibility state. Used to
162      * reserve space for the bubble bar when transitioning taskbar view into overflow.
163      */
164     public float getBubbleBarMaxCollapsedWidthIfVisible() {
165         return mControllers.bubbleControllers
166                 .filter(c -> !c.bubbleBarViewController.isHiddenForNoBubbles())
167                 .map(c -> c.bubbleBarViewController.getCollapsedWidthWithMaxVisibleBubbles())
168                 .orElse(0f);
169     }
170 
171     /** Returns true if bubble bar controllers are present. */
172     public boolean isBubbleBarEnabled() {
173         return mControllers.bubbleControllers.isPresent();
174     }
175 
176     /** Returns on click listener for the taskbar overflow view. */
177     public View.OnClickListener getOverflowOnClickListener() {
178         return new View.OnClickListener() {
179             @Override
180             public void onClick(View v) {
181                 toggleKeyboardQuickSwitchView();
182             }
183         };
184     }
185 
186     /** Returns on long click listener for the taskbar overflow view. */
187     public View.OnLongClickListener getOverflowOnLongClickListener() {
188         return new View.OnLongClickListener() {
189             @Override
190             public boolean onLongClick(View v) {
191                 toggleKeyboardQuickSwitchView();
192                 return true;
193             }
194         };
195     }
196 
197     private void toggleKeyboardQuickSwitchView() {
198         if (mTaskbarView.getTaskbarOverflowView() != null) {
199             mTaskbarView.getTaskbarOverflowView().setIsActive(
200                     !mTaskbarView.getTaskbarOverflowView().getIsActive());
201             mControllers.taskbarAutohideSuspendController
202                     .updateFlag(FLAG_AUTOHIDE_SUSPEND_TASKBAR_OVERFLOW,
203                             mTaskbarView.getTaskbarOverflowView().getIsActive());
204         }
205         mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
206                 mControllers.taskbarViewController.getTaskIdsForPinnedApps(),
207                 this::onKeyboardQuickSwitchViewClosed);
208     }
209 
210     private void onKeyboardQuickSwitchViewClosed() {
211         if (mTaskbarView.getTaskbarOverflowView() != null) {
212             mTaskbarView.getTaskbarOverflowView().setIsActive(false);
213         }
214         mControllers.taskbarAutohideSuspendController.updateFlag(
215                 FLAG_AUTOHIDE_SUSPEND_TASKBAR_OVERFLOW, false);
216     }
217 
218     private float getDividerCenterX() {
219         View divider = mTaskbarView.getTaskbarDividerViewContainer();
220         if (divider == null) {
221             return 0.0f;
222         }
223         return divider.getX() + (float) divider.getWidth() / 2;
224     }
225 
226     private class TaskbarViewGestureListener extends GestureDetector.SimpleOnGestureListener {
227         @Override
228         public boolean onDown(@NonNull MotionEvent event) {
229             if (event.isFromSource(InputDevice.SOURCE_MOUSE)
230                     && event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
231                 maybeShowPinningView(event);
232             }
233             return true;
234         }
235 
236         @Override
237         public boolean onSingleTapUp(@NonNull MotionEvent event) {
238             return true;
239         }
240 
241         @Override
242         public void onLongPress(@NonNull MotionEvent event) {
243             if (maybeShowPinningView(event)) {
244                 mTaskbarView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
245             }
246         }
247 
248         /** Returns true if the taskbar pinning popup view was shown for {@code event}. */
249         private boolean maybeShowPinningView(@NonNull MotionEvent event) {
250             if (!mActivity.isPinnedTaskbar() || mTaskbarView.isEventOverAnyItem(event)) {
251                 return false;
252             }
253             mControllers.taskbarPinningController.showPinningView(mTaskbarView, event.getRawX());
254             return true;
255         }
256     }
257 }
258