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