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