• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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