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(mApps, mAppsModelFlags, mPackageUserKeytoUidMap); 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 /** Updates the current notification dots. */ updateNotificationDots(Predicate<PackageUserKey> updatedDots)123 public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) { 124 if (mAppsView != null) { 125 mAppsView.getAppsStore().updateNotificationDots(updatedDots); 126 } 127 } 128 129 /** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */ toggle()130 public void toggle() { 131 if (isOpen()) { 132 mSlideInView.close(true); 133 } else { 134 show(true); 135 } 136 } 137 138 /** Returns {@code true} if All Apps is open. */ isOpen()139 public boolean isOpen() { 140 return mSlideInView != null && mSlideInView.isOpen(); 141 } 142 show(boolean animate)143 private void show(boolean animate) { 144 if (mAppsView != null) { 145 return; 146 } 147 // mControllers and getSharedState should never be null here. Do not handle null-pointer 148 // to catch invalid states. 149 mControllers.getSharedState().allAppsVisible = true; 150 151 mOverlayContext = mControllers.taskbarOverlayController.requestWindow(); 152 153 // Initialize search session for All Apps. 154 mSearchSessionController = TaskbarSearchSessionController.newInstance(mOverlayContext); 155 mOverlayContext.setSearchSessionController(mSearchSessionController); 156 mSearchSessionController.setZeroStatePredictedItems(mPredictedApps); 157 if (mZeroStateSearchSuggestions != null) { 158 mSearchSessionController.setZeroStateSearchSuggestions(mZeroStateSearchSuggestions); 159 } 160 mSearchSessionController.startLifecycle(); 161 162 mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate( 163 R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false); 164 mSlideInView.addOnCloseListener(() -> { 165 mControllers.getSharedState().allAppsVisible = false; 166 cleanUpOverlay(); 167 }); 168 TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController( 169 mOverlayContext, mSlideInView, mControllers, mSearchSessionController); 170 171 viewController.show(animate); 172 mAppsView = mOverlayContext.getAppsView(); 173 mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap); 174 mAppsView.getFloatingHeaderView() 175 .findFixedRowByType(PredictionRowView.class) 176 .setPredictedApps(mPredictedApps); 177 // 1 alternative that would be more work: 178 // Create a shared drag layer between taskbar and taskbarAllApps so that when dragging 179 // starts and taskbarAllApps can close, but the drag layer that the view is being dragged in 180 // doesn't also close 181 mOverlayContext.getDragController().setDisallowGlobalDrag(mDisallowGlobalDrag); 182 mOverlayContext.getDragController().setDisallowLongClick(mDisallowLongClick); 183 } 184 cleanUpOverlay()185 private void cleanUpOverlay() { 186 // Floating search bar is added to the drag layer in ActivityAllAppsContainerView onAttach; 187 // removed here as this is a special case that we remove the all apps panel. 188 if (mAppsView != null && mOverlayContext != null 189 && mAppsView.getSearchUiDelegate().isSearchBarFloating()) { 190 mOverlayContext.getDragLayer().removeView(mAppsView.getSearchView()); 191 mAppsView.getSearchUiDelegate().onDestroySearchBar(); 192 } 193 if (mSearchSessionController != null) { 194 mSearchSessionController.onDestroy(); 195 mSearchSessionController = null; 196 } 197 if (mOverlayContext != null) { 198 mOverlayContext.setSearchSessionController(null); 199 mOverlayContext = null; 200 } 201 mSlideInView = null; 202 mAppsView = null; 203 } 204 205 @VisibleForTesting getTaskbarAllAppsTopPadding()206 public int getTaskbarAllAppsTopPadding() { 207 // Allow null-pointer since this should only be null if the apps view is not showing. 208 return mAppsView.getActiveRecyclerView().getClipBounds().top; 209 } 210 211 @VisibleForTesting getTaskbarAllAppsScroll()212 public int getTaskbarAllAppsScroll() { 213 // Allow null-pointer since this should only be null if the apps view is not showing. 214 return mAppsView.getActiveRecyclerView().computeVerticalScrollOffset(); 215 } 216 217 /** @see TaskbarSearchSessionController#createPreDragConditionForSearch(View) */ 218 @Nullable createPreDragConditionForSearch(View view)219 public PreDragCondition createPreDragConditionForSearch(View view) { 220 return mSearchSessionController != null 221 ? mSearchSessionController.createPreDragConditionForSearch(view) 222 : null; 223 } 224 } 225