blob: c9a6ea9939f566392fd3e14329ad7be5011e3dd6 [file] [log] [blame]
Winson Chung6519c1b2017-10-13 17:12:56 -07001/*
2 * Copyright (C) 2015 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
17package com.android.systemui.shared.system;
18
19import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
Winson Chungb05b3982017-11-01 18:02:43 -070020import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
22import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
23import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
24import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
25import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
Winson Chung6519c1b2017-10-13 17:12:56 -070026
27import android.annotation.NonNull;
28import android.app.ActivityManager;
29import android.app.ActivityManager.RecentTaskInfo;
Winson Chungb05b3982017-11-01 18:02:43 -070030import android.app.ActivityOptions;
Winson Chung6519c1b2017-10-13 17:12:56 -070031import android.app.AppGlobals;
Winson Chung2cf6ad82017-11-09 17:36:59 -080032import android.app.IAssistDataReceiver;
Tony Wickhamfb63fe82018-01-16 12:14:06 -080033import android.app.WindowConfiguration.ActivityType;
Winson Chung6519c1b2017-10-13 17:12:56 -070034import android.content.Context;
35import android.content.pm.ActivityInfo;
36import android.content.pm.ApplicationInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.UserInfo;
39import android.content.res.Resources;
40import android.content.res.Resources.NotFoundException;
41import android.graphics.Bitmap;
42import android.graphics.drawable.BitmapDrawable;
43import android.graphics.drawable.Drawable;
Winson Chung1c5aeff2017-11-08 17:32:28 -080044import android.os.Bundle;
Winson Chungb05b3982017-11-01 18:02:43 -070045import android.os.Handler;
Winson Chung2cf6ad82017-11-09 17:36:59 -080046import android.os.Looper;
Winson Chung6519c1b2017-10-13 17:12:56 -070047import android.os.RemoteException;
48import android.os.UserHandle;
49import android.util.IconDrawableFactory;
50import android.util.Log;
51
Winson Chungb05b3982017-11-01 18:02:43 -070052import com.android.systemui.shared.recents.model.Task;
53import com.android.systemui.shared.recents.model.Task.TaskKey;
Winson Chung6519c1b2017-10-13 17:12:56 -070054import com.android.systemui.shared.recents.model.ThumbnailData;
55
56import java.util.ArrayList;
57import java.util.List;
Winson Chungb05b3982017-11-01 18:02:43 -070058import java.util.function.Consumer;
Winson Chung6519c1b2017-10-13 17:12:56 -070059
60public class ActivityManagerWrapper {
61
62 private static final String TAG = "ActivityManagerWrapper";
63
64 private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
65
66 private final PackageManager mPackageManager;
67 private final IconDrawableFactory mDrawableFactory;
Winson Chungaa357452017-10-31 11:35:30 -070068 private final BackgroundExecutor mBackgroundExecutor;
Winson Chung2cf6ad82017-11-09 17:36:59 -080069 private final TaskStackChangeListeners mTaskStackChangeListeners;
Winson Chung6519c1b2017-10-13 17:12:56 -070070
71 private ActivityManagerWrapper() {
72 final Context context = AppGlobals.getInitialApplication();
73 mPackageManager = context.getPackageManager();
74 mDrawableFactory = IconDrawableFactory.newInstance(context);
Winson Chungaa357452017-10-31 11:35:30 -070075 mBackgroundExecutor = BackgroundExecutor.get();
Winson Chung2cf6ad82017-11-09 17:36:59 -080076 mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
Winson Chung6519c1b2017-10-13 17:12:56 -070077 }
78
79 public static ActivityManagerWrapper getInstance() {
80 return sInstance;
81 }
82
83 /**
84 * @return the current user's id.
85 */
86 public int getCurrentUserId() {
87 UserInfo ui;
88 try {
89 ui = ActivityManager.getService().getCurrentUser();
90 return ui != null ? ui.id : 0;
91 } catch (RemoteException e) {
92 throw e.rethrowFromSystemServer();
93 }
94 }
95
96 /**
Winson Chungb05b3982017-11-01 18:02:43 -070097 * @return the top running task (can be {@code null}).
98 */
99 public ActivityManager.RunningTaskInfo getRunningTask() {
Tony Wickhamfb63fe82018-01-16 12:14:06 -0800100 return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */);
101 }
102
103 public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) {
Winson Chungb05b3982017-11-01 18:02:43 -0700104 // Note: The set of running tasks from the system is ordered by recency
105 try {
106 List<ActivityManager.RunningTaskInfo> tasks =
Tony Wickhamfb63fe82018-01-16 12:14:06 -0800107 ActivityManager.getService().getFilteredTasks(1, ignoreActivityType,
Winson Chungb05b3982017-11-01 18:02:43 -0700108 WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
109 if (tasks.isEmpty()) {
110 return null;
111 }
112 return tasks.get(0);
113 } catch (RemoteException e) {
114 return null;
115 }
116 }
117
118 /**
Winson Chung6519c1b2017-10-13 17:12:56 -0700119 * @return a list of the recents tasks.
120 */
121 public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
122 try {
123 return ActivityManager.getService().getRecentTasks(numTasks,
124 RECENT_IGNORE_UNAVAILABLE, userId).getList();
125 } catch (RemoteException e) {
126 Log.e(TAG, "Failed to get recent tasks", e);
127 return new ArrayList<>();
128 }
129 }
130
131 /**
132 * @return the task snapshot for the given {@param taskId}.
133 */
134 public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean reducedResolution) {
135 ActivityManager.TaskSnapshot snapshot = null;
136 try {
137 snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
138 } catch (RemoteException e) {
139 Log.w(TAG, "Failed to retrieve task snapshot", e);
140 }
141 if (snapshot != null) {
142 return new ThumbnailData(snapshot);
143 } else {
144 return new ThumbnailData();
145 }
146 }
147
148 /**
149 * @return the task description icon, loading and badging it if it necessary.
150 */
151 public Drawable getBadgedTaskDescriptionIcon(Context context,
152 ActivityManager.TaskDescription taskDescription, int userId, Resources res) {
153 Bitmap tdIcon = taskDescription.getInMemoryIcon();
154 Drawable dIcon = null;
155 if (tdIcon != null) {
156 dIcon = new BitmapDrawable(res, tdIcon);
157 } else if (taskDescription.getIconResource() != 0) {
158 try {
159 dIcon = context.getDrawable(taskDescription.getIconResource());
160 } catch (NotFoundException e) {
161 Log.e(TAG, "Could not find icon drawable from resource", e);
162 }
163 } else {
164 tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
165 taskDescription.getIconFilename(), userId);
166 if (tdIcon != null) {
167 dIcon = new BitmapDrawable(res, tdIcon);
168 }
169 }
170 if (dIcon != null) {
171 return getBadgedIcon(dIcon, userId);
172 }
173 return null;
174 }
175
176 /**
177 * @return the given icon for a user, badging if necessary.
178 */
179 private Drawable getBadgedIcon(Drawable icon, int userId) {
180 if (userId != UserHandle.myUserId()) {
181 icon = mPackageManager.getUserBadgedIcon(icon, new UserHandle(userId));
182 }
183 return icon;
184 }
185
186 /**
187 * @return the activity icon for the ActivityInfo for a user, badging if necessary.
188 */
189 public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
190 return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
191 }
192
193 /**
194 * @return the application icon for the ApplicationInfo for a user, badging if necessary.
195 */
196 public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
197 return mDrawableFactory.getBadgedIcon(appInfo, userId);
198 }
199
200 /**
201 * @return the activity label, badging if necessary.
202 */
203 public String getBadgedActivityLabel(ActivityInfo info, int userId) {
204 return getBadgedLabel(info.loadLabel(mPackageManager).toString(), userId);
205 }
206
207 /**
208 * @return the application label, badging if necessary.
209 */
210 public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
211 return getBadgedLabel(appInfo.loadLabel(mPackageManager).toString(), userId);
212 }
213
214 /**
215 * @return the content description for a given task, badging it if necessary. The content
216 * description joins the app and activity labels.
217 */
218 public String getBadgedContentDescription(ActivityInfo info, int userId,
219 ActivityManager.TaskDescription td) {
220 String activityLabel;
221 if (td != null && td.getLabel() != null) {
222 activityLabel = td.getLabel();
223 } else {
224 activityLabel = info.loadLabel(mPackageManager).toString();
225 }
226 String applicationLabel = info.applicationInfo.loadLabel(mPackageManager).toString();
227 String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
228 return applicationLabel.equals(activityLabel)
229 ? badgedApplicationLabel
230 : badgedApplicationLabel + " " + activityLabel;
231 }
232
233 /**
234 * @return the given label for a user, badging if necessary.
235 */
236 private String getBadgedLabel(String label, int userId) {
237 if (userId != UserHandle.myUserId()) {
238 label = mPackageManager.getUserBadgedLabel(label, new UserHandle(userId)).toString();
239 }
240 return label;
241 }
Winson Chungaa357452017-10-31 11:35:30 -0700242
243 /**
Winson Chung2cf6ad82017-11-09 17:36:59 -0800244 * Starts the recents activity. The caller should manage the thread on which this is called.
Winson Chung1c5aeff2017-11-08 17:32:28 -0800245 */
Winson Chung2cf6ad82017-11-09 17:36:59 -0800246 public void startRecentsActivity(AssistDataReceiverCompat assistDataReceiver, Bundle options,
Winson Chung1c5aeff2017-11-08 17:32:28 -0800247 ActivityOptions opts, int userId, Consumer<Boolean> resultCallback,
248 Handler resultCallbackHandler) {
249 Bundle activityOptions = opts != null ? opts.toBundle() : null;
Winson Chung2cf6ad82017-11-09 17:36:59 -0800250 try {
251 IAssistDataReceiver receiver = null;
252 if (assistDataReceiver != null) {
253 receiver = new IAssistDataReceiver.Stub() {
254 public void onHandleAssistData(Bundle resultData) {
255 assistDataReceiver.onHandleAssistData(resultData);
Winson Chung1c5aeff2017-11-08 17:32:28 -0800256 }
Winson Chung2cf6ad82017-11-09 17:36:59 -0800257 public void onHandleAssistScreenshot(Bitmap screenshot) {
258 assistDataReceiver.onHandleAssistScreenshot(screenshot);
Winson Chung1c5aeff2017-11-08 17:32:28 -0800259 }
Winson Chung2cf6ad82017-11-09 17:36:59 -0800260 };
Winson Chung1c5aeff2017-11-08 17:32:28 -0800261 }
Winson Chung2cf6ad82017-11-09 17:36:59 -0800262 ActivityManager.getService().startRecentsActivity(receiver, options, activityOptions,
263 userId);
264 if (resultCallback != null) {
265 resultCallbackHandler.post(new Runnable() {
266 @Override
267 public void run() {
268 resultCallback.accept(true);
269 }
270 });
271 }
272 } catch (Exception e) {
273 if (resultCallback != null) {
274 resultCallbackHandler.post(new Runnable() {
275 @Override
276 public void run() {
277 resultCallback.accept(false);
278 }
279 });
280 }
281 }
Winson Chung1c5aeff2017-11-08 17:32:28 -0800282 }
283
284 /**
Winson Chungb05b3982017-11-01 18:02:43 -0700285 * Starts a task from Recents.
286 *
Winson Chung2cf6ad82017-11-09 17:36:59 -0800287 * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
Winson Chungb05b3982017-11-01 18:02:43 -0700288 */
Winson Chung2cf6ad82017-11-09 17:36:59 -0800289 public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
Winson Chungb05b3982017-11-01 18:02:43 -0700290 Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
Winson Chung2cf6ad82017-11-09 17:36:59 -0800291 startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
Winson Chungb05b3982017-11-01 18:02:43 -0700292 ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
293 }
294
295 /**
296 * Starts a task from Recents.
297 *
298 * @param resultCallback The result success callback
299 * @param resultCallbackHandler The handler to receive the result callback
300 */
Winson Chung2cf6ad82017-11-09 17:36:59 -0800301 public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
Winson Chungb05b3982017-11-01 18:02:43 -0700302 int windowingMode, int activityType, Consumer<Boolean> resultCallback,
303 Handler resultCallbackHandler) {
304 if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
305 // We show non-visible docked tasks in Recents, but we always want to launch
306 // them in the fullscreen stack.
307 if (options == null) {
308 options = ActivityOptions.makeBasic();
309 }
310 options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
311 } else if (windowingMode != WINDOWING_MODE_UNDEFINED
312 || activityType != ACTIVITY_TYPE_UNDEFINED) {
313 if (options == null) {
314 options = ActivityOptions.makeBasic();
315 }
316 options.setLaunchWindowingMode(windowingMode);
317 options.setLaunchActivityType(activityType);
318 }
319 final ActivityOptions finalOptions = options;
320
321 // Execute this from another thread such that we can do other things (like caching the
322 // bitmap for the thumbnail) while AM is busy starting our activity.
Winson Chung1c5aeff2017-11-08 17:32:28 -0800323 mBackgroundExecutor.submit(new Runnable() {
324 @Override
325 public void run() {
Winson Chung42ce9002017-11-17 13:04:07 -0800326 boolean result = false;
Winson Chung1c5aeff2017-11-08 17:32:28 -0800327 try {
Winson Chung42ce9002017-11-17 13:04:07 -0800328 result = startActivityFromRecents(taskKey.id, finalOptions);
Winson Chung1c5aeff2017-11-08 17:32:28 -0800329 } catch (Exception e) {
Winson Chung42ce9002017-11-17 13:04:07 -0800330 // Fall through
331 }
332 final boolean finalResult = result;
333 if (resultCallback != null) {
334 resultCallbackHandler.post(new Runnable() {
335 @Override
336 public void run() {
337 resultCallback.accept(finalResult);
338 }
339 });
Winson Chungb05b3982017-11-01 18:02:43 -0700340 }
341 }
342 });
343 }
344
345 /**
Winson Chung42ce9002017-11-17 13:04:07 -0800346 * Starts a task from Recents synchronously.
347 */
348 public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
349 try {
350 Bundle optsBundle = options == null ? null : options.toBundle();
351 ActivityManager.getService().startActivityFromRecents(taskId, optsBundle);
352 return true;
353 } catch (Exception e) {
354 return false;
355 }
356 }
357
358 /**
Winson Chung2cf6ad82017-11-09 17:36:59 -0800359 * Registers a task stack listener with the system.
360 * This should be called on the main thread.
361 */
362 public void registerTaskStackListener(TaskStackChangeListener listener) {
363 synchronized (mTaskStackChangeListeners) {
364 mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
365 }
366 }
367
368 /**
369 * Unregisters a task stack listener with the system.
370 * This should be called on the main thread.
371 */
372 public void unregisterTaskStackListener(TaskStackChangeListener listener) {
373 synchronized (mTaskStackChangeListeners) {
374 mTaskStackChangeListeners.removeListener(listener);
375 }
376 }
377
378 /**
Winson Chungaa357452017-10-31 11:35:30 -0700379 * Requests that the system close any open system windows (including other SystemUI).
380 */
381 public void closeSystemWindows(String reason) {
Winson Chung1c5aeff2017-11-08 17:32:28 -0800382 mBackgroundExecutor.submit(new Runnable() {
383 @Override
384 public void run() {
385 try {
386 ActivityManager.getService().closeSystemDialogs(reason);
387 } catch (RemoteException e) {
388 Log.w(TAG, "Failed to close system windows", e);
389 }
Winson Chungaa357452017-10-31 11:35:30 -0700390 }
391 });
392 }
393
394 /**
395 * Removes a task by id.
396 */
397 public void removeTask(int taskId) {
Winson Chung1c5aeff2017-11-08 17:32:28 -0800398 mBackgroundExecutor.submit(new Runnable() {
399 @Override
400 public void run() {
401 try {
402 ActivityManager.getService().removeTask(taskId);
403 } catch (RemoteException e) {
404 Log.w(TAG, "Failed to remove task=" + taskId, e);
405 }
Winson Chungaa357452017-10-31 11:35:30 -0700406 }
407 });
408 }
409
410 /**
411 * Cancels the current window transtion to/from Recents for the given task id.
412 */
413 public void cancelWindowTransition(int taskId) {
414 try {
415 ActivityManager.getService().cancelTaskWindowTransition(taskId);
416 } catch (RemoteException e) {
417 Log.w(TAG, "Failed to cancel window transition for task=" + taskId, e);
418 }
419 }
Winson Chung6519c1b2017-10-13 17:12:56 -0700420}