1 /* 2 * Copyright (C) 2022 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 package com.android.launcher3.taskbar.allapps; 17 18 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; 19 20 import android.view.View; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 import androidx.annotation.VisibleForTesting; 25 26 import com.android.launcher3.R; 27 import com.android.launcher3.appprediction.PredictionRowView; 28 import com.android.launcher3.dragndrop.DragOptions.PreDragCondition; 29 import com.android.launcher3.model.data.AppInfo; 30 import com.android.launcher3.model.data.ItemInfo; 31 import com.android.launcher3.taskbar.TaskbarControllers; 32 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; 33 import com.android.launcher3.util.PackageUserKey; 34 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.function.Predicate; 39 /** 40 * Handles the all apps overlay window initialization, updates, and its data. 41 * <p> 42 * All apps is in an application overlay window instead of taskbar's navigation bar panel window, 43 * because a navigation bar panel is higher than UI components that all apps should be below such as 44 * the notification tray. 45 * <p> 46 * The all apps window is created and destroyed upon opening and closing all apps, respectively. 47 * Application data may be bound while the window does not exist, so this controller will store 48 * the models for the next all apps session. 49 */ 50 public final class TaskbarAllAppsController { 51 52 private TaskbarControllers mControllers; 53 private @Nullable TaskbarOverlayContext mOverlayContext; 54 private @Nullable TaskbarAllAppsSlideInView mSlideInView; 55 private @Nullable TaskbarAllAppsContainerView mAppsView; 56 private @Nullable TaskbarSearchSessionController mSearchSessionController; 57 58 // Application data models. 59 private @NonNull AppInfo[] mApps = EMPTY_ARRAY; 60 private int mAppsModelFlags; 61 private @NonNull List<ItemInfo> mPredictedApps = Collections.emptyList(); 62 private @Nullable List<ItemInfo> mZeroStateSearchSuggestions; 63 private boolean mDisallowGlobalDrag; 64 private boolean mDisallowLongClick; 65 66 private Map<PackageUserKey, Integer> mPackageUserKeytoUidMap = Collections.emptyMap(); 67 68 /** Initialize the controller. */ init(TaskbarControllers controllers, boolean allAppsVisible)69 public void init(TaskbarControllers controllers, boolean allAppsVisible) { 70 mControllers = controllers; 71 72 /* 73 * Recreate All Apps if it was open in the previous Taskbar instance (e.g. the configuration 74 * changed). 75 */ 76 if (allAppsVisible) { 77 show(false); 78 } 79 } 80 81 /** Clean up the controller. */ onDestroy()82 public void onDestroy() { 83 cleanUpOverlay(); 84 } 85 86 /** Updates the current {@link AppInfo} instances. */ setApps(@ullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map)87 public void setApps(@Nullable AppInfo[] apps, int flags, Map<PackageUserKey, Integer> map) { 88 mApps = apps == null ? EMPTY_ARRAY : apps; 89 mAppsModelFlags = flags; 90 mPackageUserKeytoUidMap = map; 91 if (mAppsView != null) { 92 mAppsView.getAppsStore().setApps( 93 mApps, mAppsModelFlags, mPackageUserKeytoUidMap, false); 94 } 95 } 96 setDisallowGlobalDrag(boolean disableDragForOverviewState)97 public void setDisallowGlobalDrag(boolean disableDragForOverviewState) { 98 mDisallowGlobalDrag = disableDragForOverviewState; 99 } 100 setDisallowLongClick(boolean disallowLongClick)101 public void setDisallowLongClick(boolean disallowLongClick) { 102 mDisallowLongClick = disallowLongClick; 103 } 104 105 /** Updates the current predictions. */ setPredictedApps(List<ItemInfo> predictedApps)106 public void setPredictedApps(List<ItemInfo> predictedApps) { 107 mPredictedApps = predictedApps; 108 if (mAppsView != null) { 109 mAppsView.getFloatingHeaderView() 110 .findFixedRowByType(PredictionRowView.class) 111 .setPredictedApps(mPredictedApps); 112 } 113 if (mSearchSessionController != null) { 114 mSearchSessionController.setZeroStatePredictedItems(predictedApps); 115 } 116 } 117 118 /** Updates the current search suggestions. */ setZeroStateSearchSuggestions(List<ItemInfo> zeroStateSearchSuggestions)119 public void setZeroStateSearchSuggestions(List<ItemInfo> zeroStateSearchSuggestions) { 120 mZeroStateSearchSuggestions = zeroStateSearchSuggestions; 121 } 122 123 /** Updates the current notification dots. */ updateNotificationDots(Predicate<PackageUserKey> updatedDots)124 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 125 if (mAppsView != null) { 126 mAppsView.getAppsStore().updateNotificationDots(updatedDots); 127 } 128 } 129 130 /** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */ toggle()131 public void toggle() { 132 toggle(false); 133 } 134 135 /** Toggles visibility of {@link TaskbarAllAppsContainerView} with the keyboard for search. */ toggleSearch()136 public void toggleSearch() { 137 toggle(true); 138 } 139 toggle(boolean showKeyboard)140 private void toggle(boolean showKeyboard) { 141 if (isOpen()) { 142 mSlideInView.close(true); 143 } else { 144 show(true, showKeyboard); 145 } 146 } 147 148 /** Returns {@code true} if All Apps is open. */ isOpen()149 public boolean isOpen() { 150 return mSlideInView != null && mSlideInView.isOpen(); 151 } 152 show(boolean animate)153 private void show(boolean animate) { 154 show(animate, false); 155 } 156 show(boolean animate, boolean showKeyboard)157 private void show(boolean animate, boolean showKeyboard) { 158 if (mAppsView != null) { 159 return; 160 } 161 mOverlayContext = mControllers.taskbarOverlayController.requestWindow(); 162 163 // Initialize search session for All Apps. 164 mSearchSessionController = TaskbarSearchSessionController.newInstance(mOverlayContext); 165 mOverlayContext.setSearchSessionController(mSearchSessionController); 166 mSearchSessionController.setZeroStatePredictedItems(mPredictedApps); 167 if (mZeroStateSearchSuggestions != null) { 168 mSearchSessionController.setZeroStateSearchSuggestions(mZeroStateSearchSuggestions); 169 } 170 mSearchSessionController.startLifecycle(); 171 172 mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate( 173 R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false); 174 // Ensures All Apps gets touch events in case it is not the top floating view. Floating 175 // views above it may not be able to intercept the touch, so All Apps should try to. 176 mOverlayContext.getDragLayer().addTouchController(mSlideInView); 177 mSlideInView.addOnCloseListener(this::cleanUpOverlay); 178 TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController( 179 mOverlayContext, 180 mSlideInView, 181 mControllers, 182 mSearchSessionController, 183 showKeyboard); 184 185 viewController.show(animate); 186 mAppsView = mOverlayContext.getAppsView(); 187 mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap, false); 188 mAppsView.getFloatingHeaderView() 189 .findFixedRowByType(PredictionRowView.class) 190 .setPredictedApps(mPredictedApps); 191 // 1 alternative that would be more work: 192 // Create a shared drag layer between taskbar and taskbarAllApps so that when dragging 193 // starts and taskbarAllApps can close, but the drag layer that the view is being dragged in 194 // doesn't also close 195 mOverlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag); 196 mOverlayContext.getDragController().setDisallowLongClick(mDisallowLongClick); 197 } 198 cleanUpOverlay()199 private void cleanUpOverlay() { 200 // Floating search bar is added to the drag layer in ActivityAllAppsContainerView onAttach; 201 // removed here as this is a special case that we remove the all apps panel. 202 if (mAppsView != null && mOverlayContext != null 203 && mAppsView.getSearchUiDelegate().isSearchBarFloating()) { 204 mOverlayContext.getDragLayer().removeView(mAppsView.getSearchView()); 205 mAppsView.getSearchUiDelegate().onDestroySearchBar(); 206 } 207 if (mSearchSessionController != null) { 208 mSearchSessionController.onDestroy(); 209 mSearchSessionController = null; 210 } 211 if (mOverlayContext != null) { 212 mOverlayContext.getDragLayer().removeTouchController(mSlideInView); 213 mOverlayContext.setSearchSessionController(null); 214 mOverlayContext = null; 215 } 216 mSlideInView = null; 217 mAppsView = null; 218 } 219 220 @VisibleForTesting getTaskbarAllAppsTopPadding()221 public int getTaskbarAllAppsTopPadding() { 222 // Allow null-pointer since this should only be null if the apps view is not showing. 223 return mAppsView.getActiveRecyclerView().getClipBounds().top; 224 } 225 226 @VisibleForTesting getTaskbarAllAppsScroll()227 public int getTaskbarAllAppsScroll() { 228 // Allow null-pointer since this should only be null if the apps view is not showing. 229 return mAppsView.getActiveRecyclerView().computeVerticalScrollOffset(); 230 } 231 232 /** @see TaskbarSearchSessionController#createPreDragConditionForSearch(View) */ 233 @Nullable createPreDragConditionForSearch(View view)234 public PreDragCondition createPreDragConditionForSearch(View view) { 235 return mSearchSessionController != null 236 ? mSearchSessionController.createPreDragConditionForSearch(view) 237 : null; 238 } 239 } 240