• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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;
17 
18 import android.content.ComponentName;
19 import android.content.pm.ActivityInfo;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 
24 import com.android.launcher3.R;
25 import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
26 import com.android.quickstep.RecentsModel;
27 import com.android.quickstep.util.GroupTask;
28 import com.android.systemui.shared.recents.model.Task;
29 import com.android.systemui.shared.recents.model.ThumbnailData;
30 
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.function.Consumer;
36 import java.util.stream.Collectors;
37 
38 /**
39  * Handles initialization of the {@link KeyboardQuickSwitchViewController}.
40  */
41 public final class KeyboardQuickSwitchController implements
42         TaskbarControllers.LoggableTaskbarController {
43 
44     static final int MAX_TASKS = 6;
45 
46     @NonNull private final ControllerCallbacks mControllerCallbacks = new ControllerCallbacks();
47 
48     // Initialized on init
49     @Nullable private RecentsModel mModel;
50 
51     // Used to keep track of the last requested task list id, so that we do not request to load the
52     // tasks again if we have already requested it and the task list has not changed
53     private int mTaskListChangeId = -1;
54     // Only empty before the recent tasks list has been loaded the first time
55     @NonNull private List<GroupTask> mTasks = new ArrayList<>();
56     private int mNumHiddenTasks = 0;
57 
58     // Initialized in init
59     private TaskbarControllers mControllers;
60 
61     @Nullable private KeyboardQuickSwitchViewController mQuickSwitchViewController;
62 
63     /** Initialize the controller. */
init(@onNull TaskbarControllers controllers)64     public void init(@NonNull TaskbarControllers controllers) {
65         mControllers = controllers;
66         mModel = RecentsModel.INSTANCE.get(controllers.taskbarActivityContext);
67     }
68 
onConfigurationChanged(@ctivityInfo.Config int configChanges)69     void onConfigurationChanged(@ActivityInfo.Config int configChanges) {
70         if (mQuickSwitchViewController == null) {
71             return;
72         }
73         if ((configChanges & (ActivityInfo.CONFIG_KEYBOARD
74                 | ActivityInfo.CONFIG_KEYBOARD_HIDDEN)) != 0) {
75             mQuickSwitchViewController.closeQuickSwitchView(true);
76             return;
77         }
78         int currentFocusedIndex = mQuickSwitchViewController.getCurrentFocusedIndex();
79         onDestroy();
80         if (currentFocusedIndex != -1) {
81             mControllers.taskbarActivityContext.getMainThreadHandler().post(
82                     () -> openQuickSwitchView(currentFocusedIndex));
83         }
84     }
85 
openQuickSwitchView()86     void openQuickSwitchView() {
87         openQuickSwitchView(-1);
88     }
89 
openQuickSwitchView(int currentFocusedIndex)90     private void openQuickSwitchView(int currentFocusedIndex) {
91         if (mQuickSwitchViewController != null) {
92             return;
93         }
94         TaskbarOverlayContext overlayContext =
95                 mControllers.taskbarOverlayController.requestWindow();
96         KeyboardQuickSwitchView keyboardQuickSwitchView =
97                 (KeyboardQuickSwitchView) overlayContext.getLayoutInflater()
98                         .inflate(
99                                 R.layout.keyboard_quick_switch_view,
100                                 overlayContext.getDragLayer(),
101                                 /* attachToRoot= */ false);
102         mQuickSwitchViewController = new KeyboardQuickSwitchViewController(
103                 mControllers, overlayContext, keyboardQuickSwitchView, mControllerCallbacks);
104 
105         if (mModel.isTaskListValid(mTaskListChangeId)) {
106             mQuickSwitchViewController.openQuickSwitchView(
107                     mTasks, mNumHiddenTasks, /* updateTasks= */ false, currentFocusedIndex);
108             return;
109         }
110         mTaskListChangeId = mModel.getTasks((tasks) -> {
111             // Only store MAX_TASK tasks, from most to least recent
112             Collections.reverse(tasks);
113             mTasks = tasks.stream().limit(MAX_TASKS).collect(Collectors.toList());
114             mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS);
115             mQuickSwitchViewController.openQuickSwitchView(
116                     mTasks, mNumHiddenTasks, /* updateTasks= */ true, currentFocusedIndex);
117         });
118     }
119 
closeQuickSwitchView()120     void closeQuickSwitchView() {
121         if (mQuickSwitchViewController == null) {
122             return;
123         }
124         mQuickSwitchViewController.closeQuickSwitchView(true);
125     }
126 
127     /**
128      * See {@link TaskbarUIController#launchFocusedTask()}
129      */
launchFocusedTask()130     int launchFocusedTask() {
131         // Return -1 so that the RecentsView is not incorrectly opened when the user closes the
132         // quick switch view by tapping the screen.
133         return mQuickSwitchViewController == null
134                 ? -1 : mQuickSwitchViewController.launchFocusedTask();
135     }
136 
onDestroy()137     void onDestroy() {
138         if (mQuickSwitchViewController != null) {
139             mQuickSwitchViewController.onDestroy();
140         }
141     }
142 
143     @Override
dumpLogs(String prefix, PrintWriter pw)144     public void dumpLogs(String prefix, PrintWriter pw) {
145         pw.println(prefix + "KeyboardQuickSwitchController:");
146 
147         pw.println(prefix + "\tisOpen=" + (mQuickSwitchViewController != null));
148         pw.println(prefix + "\tmNumHiddenTasks=" + mNumHiddenTasks);
149         pw.println(prefix + "\tmTaskListChangeId=" + mTaskListChangeId);
150         pw.println(prefix + "\tmTasks=[");
151         for (GroupTask task : mTasks) {
152             Task task1 = task.task1;
153             Task task2 = task.task2;
154             ComponentName cn1 = task1.getTopComponent();
155             ComponentName cn2 = task2 != null ? task2.getTopComponent() : null;
156             pw.println(prefix + "\t\tt1: (id=" + task1.key.id
157                     + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)")
158                     + " t2: (id=" + (task2 != null ? task2.key.id : "-1")
159                     + "; package=" + (cn2 != null ? cn2.getPackageName() + ")"
160                     : "no package)"));
161         }
162         pw.println(prefix + "\t]");
163 
164         if (mQuickSwitchViewController != null) {
165             mQuickSwitchViewController.dumpLogs(prefix + '\t', pw);
166         }
167     }
168 
169     class ControllerCallbacks {
170 
getTaskCount()171         int getTaskCount() {
172             return mNumHiddenTasks == 0 ? mTasks.size() : MAX_TASKS + 1;
173         }
174 
175         @Nullable
getTaskAt(int index)176         GroupTask getTaskAt(int index) {
177             return index < 0 || index >= mTasks.size() ? null : mTasks.get(index);
178         }
179 
updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback)180         void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback) {
181             mModel.getThumbnailCache().updateThumbnailInBackground(task, callback);
182         }
183 
updateTitleInBackground(Task task, Consumer<Task> callback)184         void updateTitleInBackground(Task task, Consumer<Task> callback) {
185             mModel.getIconCache().updateIconInBackground(task, callback);
186         }
187 
onCloseComplete()188         void onCloseComplete() {
189             mQuickSwitchViewController = null;
190         }
191     }
192 }
193