2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.recents.misc;
19 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
20 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
21 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
22 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
23 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
24 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
26 import android.app.ActivityManager;
27 import android.app.ActivityManagerNative;
28 import android.app.ActivityOptions;
29 import android.app.AppGlobals;
30 import android.app.IActivityManager;
31 import android.app.ITaskStackListener;
32 import android.app.UiModeManager;
33 import android.content.ComponentName;
34 import android.content.ContentResolver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.pm.ActivityInfo;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.IPackageManager;
40 import android.content.pm.PackageManager;
41 import android.content.pm.ResolveInfo;
42 import android.content.res.Configuration;
43 import android.content.res.Resources;
44 import android.graphics.Bitmap;
45 import android.graphics.BitmapFactory;
46 import android.graphics.Canvas;
47 import android.graphics.Color;
48 import android.graphics.Paint;
49 import android.graphics.Point;
50 import android.graphics.PorterDuff;
51 import android.graphics.PorterDuffXfermode;
52 import android.graphics.Rect;
53 import android.graphics.drawable.BitmapDrawable;
54 import android.graphics.drawable.ColorDrawable;
55 import android.graphics.drawable.Drawable;
56 import android.os.Handler;
57 import android.os.IRemoteCallback;
58 import android.os.Looper;
59 import android.os.Message;
60 import android.os.ParcelFileDescriptor;
61 import android.os.RemoteException;
62 import android.os.SystemProperties;
63 import android.os.UserHandle;
64 import android.os.UserManager;
65 import android.provider.Settings;
66 import android.util.ArraySet;
67 import android.util.Log;
68 import android.util.MutableBoolean;
69 import android.view.Display;
70 import android.view.IAppTransitionAnimationSpecsFuture;
71 import android.view.IDockedStackListener;
72 import android.view.WindowManager;
73 import android.view.WindowManager.KeyboardShortcutsReceiver;
74 import android.view.WindowManagerGlobal;
75 import android.view.accessibility.AccessibilityManager;
77 import com.android.internal.app.AssistUtils;
78 import com.android.internal.os.BackgroundThread;
79 import com.android.systemui.R;
80 import com.android.systemui.recents.RecentsDebugFlags;
81 import com.android.systemui.recents.RecentsImpl;
82 import com.android.systemui.recents.model.Task;
83 import com.android.systemui.recents.tv.RecentsTvImpl;
84 import com.android.systemui.recents.model.ThumbnailData;
86 import java.io.IOException;
87 import java.util.ArrayList;
88 import java.util.Collections;
89 import java.util.Iterator;
90 import java.util.List;
91 import java.util.Random;
94 * Acts as a shim around the real system services that we need to access data from, and provides
95 * a point of injection when testing UI.
97 public class SystemServicesProxy {
98 final static String TAG = "SystemServicesProxy";
100 final static BitmapFactory.Options sBitmapOptions;
102 sBitmapOptions = new BitmapFactory.Options();
103 sBitmapOptions.inMutable = true;
104 sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
107 final static List<String> sRecentsBlacklist;
109 sRecentsBlacklist = new ArrayList<>();
110 sRecentsBlacklist.add("com.android.systemui.tv.pip.PipOnboardingActivity");
111 sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
114 private static SystemServicesProxy sSystemServicesProxy;
116 AccessibilityManager mAccm;
118 IActivityManager mIam;
120 IPackageManager mIpm;
121 AssistUtils mAssistUtils;
125 String mRecentsPackage;
126 ComponentName mAssistComponent;
129 boolean mHasFreeformWorkspaceSupport;
132 int mDummyThumbnailWidth;
133 int mDummyThumbnailHeight;
134 Paint mBgProtectionPaint;
135 Canvas mBgProtectionCanvas;
137 private final Handler mHandler = new H();
140 * An abstract class to track task stack changes.
141 * Classes should implement this instead of {@link android.app.ITaskStackListener}
142 * to reduce IPC calls from system services. These callbacks will be called on the main thread.
144 public abstract static class TaskStackListener {
145 public void onTaskStackChanged() { }
146 public void onActivityPinned() { }
147 public void onPinnedActivityRestartAttempt() { }
148 public void onPinnedStackAnimationEnded() { }
149 public void onActivityForcedResizable(String packageName, int taskId) { }
150 public void onActivityDismissingDockedStack() { }
154 * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
155 * ActivityManagerNative.
156 * This simply passes callbacks to listeners through {@link H}.
158 private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
160 public void onTaskStackChanged() throws RemoteException {
161 mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
162 mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
166 public void onActivityPinned() throws RemoteException {
167 mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
168 mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED);
172 public void onPinnedActivityRestartAttempt() throws RemoteException{
173 mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
174 mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
178 public void onPinnedStackAnimationEnded() throws RemoteException {
179 mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
180 mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
184 public void onActivityForcedResizable(String packageName, int taskId)
185 throws RemoteException {
186 mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
191 public void onActivityDismissingDockedStack() throws RemoteException {
192 mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
197 * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
199 private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
201 /** Private constructor */
202 private SystemServicesProxy(Context context) {
203 mAccm = AccessibilityManager.getInstance(context);
204 mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
205 mIam = ActivityManagerNative.getDefault();
206 mPm = context.getPackageManager();
207 mIpm = AppGlobals.getPackageManager();
208 mAssistUtils = new AssistUtils(context);
209 mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
210 mUm = UserManager.get(context);
211 mDisplay = mWm.getDefaultDisplay();
212 mRecentsPackage = context.getPackageName();
213 mHasFreeformWorkspaceSupport =
214 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
215 Settings.Global.getInt(context.getContentResolver(),
216 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
217 mIsSafeMode = mPm.isSafeMode();
219 // Get the dummy thumbnail width/heights
220 Resources res = context.getResources();
221 int wId = com.android.internal.R.dimen.thumbnail_width;
222 int hId = com.android.internal.R.dimen.thumbnail_height;
223 mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
224 mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
226 // Create the protection paints
227 mBgProtectionPaint = new Paint();
228 mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
229 mBgProtectionPaint.setColor(0xFFffffff);
230 mBgProtectionCanvas = new Canvas();
232 // Resolve the assist intent
233 mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
235 if (RecentsDebugFlags.Static.EnableMockTasks) {
236 // Create a dummy icon
237 mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
238 mDummyIcon.eraseColor(0xFF999999);
241 UiModeManager uiModeManager = (UiModeManager) context.
242 getSystemService(Context.UI_MODE_SERVICE);
243 if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
244 Collections.addAll(sRecentsBlacklist,
245 res.getStringArray(R.array.recents_tv_blacklist_array));
247 Collections.addAll(sRecentsBlacklist,
248 res.getStringArray(R.array.recents_blacklist_array));
253 * Returns the single instance of the {@link SystemServicesProxy}.
254 * This should only be called on the main thread.
256 public static SystemServicesProxy getInstance(Context context) {
257 if (!Looper.getMainLooper().isCurrentThread()) {
258 throw new RuntimeException("Must be called on the UI thread");
260 if (sSystemServicesProxy == null) {
261 sSystemServicesProxy = new SystemServicesProxy(context);
263 return sSystemServicesProxy;
267 * @return whether the provided {@param className} is blacklisted
269 public boolean isBlackListedActivity(String className) {
270 return sRecentsBlacklist.contains(className);
274 * Returns a list of the recents tasks.
276 * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
277 * will be visible, otherwise no excluded tasks will be
280 public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
281 boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
282 if (mAm == null) return null;
284 // If we are mocking, then create some recent tasks
285 if (RecentsDebugFlags.Static.EnableMockTasks) {
286 ArrayList<ActivityManager.RecentTaskInfo> tasks =
287 new ArrayList<ActivityManager.RecentTaskInfo>();
288 int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
289 for (int i = 0; i < count; i++) {
290 // Create a dummy component name
291 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
292 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
293 "com.android.test" + i + ".Activity");
294 String description = "" + i + " - " +
295 Long.toString(Math.abs(new Random().nextLong()), 36);
296 // Create the recent task info
297 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
298 rti.id = rti.persistentId = rti.affiliatedTaskId = i;
299 rti.baseIntent = new Intent();
300 rti.baseIntent.setComponent(cn);
301 rti.description = description;
302 rti.firstActiveTime = rti.lastActiveTime = i;
304 rti.taskDescription = new ActivityManager.TaskDescription(description,
305 Bitmap.createBitmap(mDummyIcon), null,
306 0xFF000000 | (0xFFFFFF & new Random().nextInt()),
307 0xFF000000 | (0xFFFFFF & new Random().nextInt()));
309 rti.taskDescription = new ActivityManager.TaskDescription();
316 // Remove home/recents/excluded tasks
317 int minNumTasksToQuery = 10;
318 int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
319 int flags = ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
320 ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
321 ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
322 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
323 ActivityManager.RECENT_INCLUDE_PROFILES;
324 if (includeFrontMostExcludedTask) {
325 flags |= ActivityManager.RECENT_WITH_EXCLUDED;
327 List<ActivityManager.RecentTaskInfo> tasks = null;
329 tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
330 } catch (Exception e) {
331 Log.e(TAG, "Failed to get recent tasks", e);
334 // Break early if we can't get a valid set of tasks
336 return new ArrayList<>();
339 boolean isFirstValidTask = true;
340 Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
341 while (iter.hasNext()) {
342 ActivityManager.RecentTaskInfo t = iter.next();
344 // NOTE: The order of these checks happens in the expected order of the traversal of the
347 // Remove the task if it or it's package are blacklsited
348 if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
349 sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
354 // Remove the task if it is marked as excluded, unless it is the first most task and we
355 // are requested to include it
356 boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
357 == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
358 isExcluded |= quietProfileIds.contains(t.userId);
359 if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
363 isFirstValidTask = false;
366 return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
370 * Returns the top running task.
372 public ActivityManager.RunningTaskInfo getRunningTask() {
373 List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(1);
374 if (tasks != null && !tasks.isEmpty()) {
381 * Returns whether the recents activity is currently visible.
383 public boolean isRecentsActivityVisible() {
384 return isRecentsActivityVisible(null);
388 * Returns whether the recents activity is currently visible.
390 * @param isHomeStackVisible if provided, will return whether the home stack is visible
391 * regardless of the recents visibility
393 public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
394 if (mIam == null) return false;
397 ActivityManager.StackInfo stackInfo = mIam.getStackInfo(
398 ActivityManager.StackId.HOME_STACK_ID);
399 ActivityManager.StackInfo fullscreenStackInfo = mIam.getStackInfo(
400 ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID);
401 ComponentName topActivity = stackInfo.topActivity;
402 boolean homeStackVisibleNotOccluded = stackInfo.visible;
403 if (fullscreenStackInfo != null) {
404 boolean isFullscreenStackOccludingHome = fullscreenStackInfo.visible &&
405 fullscreenStackInfo.position > stackInfo.position;
406 homeStackVisibleNotOccluded &= !isFullscreenStackOccludingHome;
408 if (isHomeStackVisible != null) {
409 isHomeStackVisible.value = homeStackVisibleNotOccluded;
411 return (homeStackVisibleNotOccluded && topActivity != null
412 && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
413 && (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)
414 || topActivity.getClassName().equals(RecentsTvImpl.RECENTS_TV_ACTIVITY)));
415 } catch (RemoteException e) {
422 * Returns whether this device has freeform workspaces.
424 public boolean hasFreeformWorkspaceSupport() {
425 return mHasFreeformWorkspaceSupport;
429 * Returns whether this device is in the safe mode.
431 public boolean isInSafeMode() {
435 /** Docks a task to the side of the screen and starts it. */
436 public boolean startTaskInDockedMode(int taskId, int createMode) {
437 if (mIam == null) return false;
440 final ActivityOptions options = ActivityOptions.makeBasic();
441 options.setDockCreateMode(createMode);
442 options.setLaunchStackId(DOCKED_STACK_ID);
443 mIam.startActivityFromRecents(taskId, options.toBundle());
445 } catch (Exception e) {
446 Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e);
451 /** Docks an already resumed task to the side of the screen. */
452 public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
458 return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
459 false /* animate */, initialBounds, true /* moveHomeStackFront */ );
460 } catch (RemoteException e) {
467 * Returns whether the given stack id is the home stack id.
469 public static boolean isHomeStack(int stackId) {
470 return stackId == HOME_STACK_ID;
474 * Returns whether the given stack id is the pinned stack id.
476 public static boolean isPinnedStack(int stackId){
477 return stackId == PINNED_STACK_ID;
481 * Returns whether the given stack id is the docked stack id.
483 public static boolean isDockedStack(int stackId) {
484 return stackId == DOCKED_STACK_ID;
488 * Returns whether the given stack id is the freeform workspace stack id.
490 public static boolean isFreeformStack(int stackId) {
491 return stackId == FREEFORM_WORKSPACE_STACK_ID;
495 * @return whether there are any docked tasks for the current user.
497 public boolean hasDockedTask() {
498 if (mIam == null) return false;
500 ActivityManager.StackInfo stackInfo = null;
502 stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
503 } catch (RemoteException e) {
507 if (stackInfo != null) {
508 int userId = getCurrentUser();
509 boolean hasUserTask = false;
510 for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
511 hasUserTask = (stackInfo.taskUserIds[i] == userId);
519 * Returns whether there is a soft nav bar.
521 public boolean hasSoftNavigationBar() {
523 return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
524 } catch (RemoteException e) {
531 * Returns whether the device has a transposed nav bar (on the right of the screen) in the
532 * current display orientation.
534 public boolean hasTransposedNavigationBar() {
535 Rect insets = new Rect();
536 getStableInsets(insets);
537 return insets.right > 0;
541 * Cancels the current window transtion to/from Recents for the given task id.
543 public void cancelWindowTransition(int taskId) {
544 if (mWm == null) return;
547 WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId);
548 } catch (RemoteException e) {
554 * Cancels the current thumbnail transtion to/from Recents for the given task id.
556 public void cancelThumbnailTransition(int taskId) {
557 if (mWm == null) return;
560 WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId);
561 } catch (RemoteException e) {
566 /** Returns the top task thumbnail for the given task id */
567 public ThumbnailData getTaskThumbnail(int taskId) {
568 if (mAm == null) return null;
569 ThumbnailData thumbnailData = new ThumbnailData();
571 // If we are mocking, then just return a dummy thumbnail
572 if (RecentsDebugFlags.Static.EnableMockTasks) {
573 thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
574 mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
575 thumbnailData.thumbnail.eraseColor(0xff333333);
576 return thumbnailData;
579 getThumbnail(taskId, thumbnailData);
580 if (thumbnailData.thumbnail != null) {
581 thumbnailData.thumbnail.setHasAlpha(false);
582 // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
583 // left pixel, then assume the whole thumbnail is transparent. Generally, proper
584 // screenshots are always composed onto a bitmap that has no alpha.
585 if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
586 mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
587 mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
588 thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
589 mBgProtectionCanvas.setBitmap(null);
590 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
593 return thumbnailData;
597 * Returns a task thumbnail from the activity manager
599 public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
604 ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
605 if (taskThumbnail == null) {
609 Bitmap thumbnail = taskThumbnail.mainThumbnail;
610 ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
611 if (thumbnail == null && descriptor != null) {
612 thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
613 null, sBitmapOptions);
615 if (descriptor != null) {
618 } catch (IOException e) {
621 thumbnailDataOut.thumbnail = thumbnail;
622 thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
626 * Moves a task into another stack.
628 public void moveTaskToStack(int taskId, int stackId) {
629 if (mIam == null) return;
632 mIam.positionTaskInStack(taskId, stackId, 0);
633 } catch (RemoteException | IllegalArgumentException e) {
638 /** Removes the task */
639 public void removeTask(final int taskId) {
640 if (mAm == null) return;
641 if (RecentsDebugFlags.Static.EnableMockTasks) return;
644 BackgroundThread.getHandler().post(new Runnable() {
647 mAm.removeTask(taskId);
653 * Sends a message to close other system windows.
655 public void sendCloseSystemWindows(String reason) {
656 if (ActivityManagerNative.isSystemReady()) {
658 mIam.closeSystemDialogs(reason);
659 } catch (RemoteException e) {
665 * Returns the activity info for a given component name.
667 * @param cn The component name of the activity.
668 * @param userId The userId of the user that this is for.
670 public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
671 if (mIpm == null) return null;
672 if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
675 return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
676 } catch (RemoteException e) {
683 * Returns the activity info for a given component name.
685 * @param cn The component name of the activity.
687 public ActivityInfo getActivityInfo(ComponentName cn) {
688 if (mPm == null) return null;
689 if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
692 return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
693 } catch (PackageManager.NameNotFoundException e) {
700 * Returns the activity label, badging if necessary.
702 public String getBadgedActivityLabel(ActivityInfo info, int userId) {
703 if (mPm == null) return null;
705 // If we are mocking, then return a mock label
706 if (RecentsDebugFlags.Static.EnableMockTasks) {
707 return "Recent Task: " + userId;
710 return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
714 * Returns the application label, badging if necessary.
716 public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
717 if (mPm == null) return null;
719 // If we are mocking, then return a mock label
720 if (RecentsDebugFlags.Static.EnableMockTasks) {
721 return "Recent Task App: " + userId;
724 return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
728 * Returns the content description for a given task, badging it if necessary. The content
729 * description joins the app and activity labels.
731 public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
732 // If we are mocking, then return a mock label
733 if (RecentsDebugFlags.Static.EnableMockTasks) {
734 return "Recent Task Content Description: " + userId;
737 String activityLabel = info.loadLabel(mPm).toString();
738 String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
739 String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
740 return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
741 : res.getString(R.string.accessibility_recents_task_header,
742 badgedApplicationLabel, activityLabel);
746 * Returns the activity icon for the ActivityInfo for a user, badging if
749 public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
750 if (mPm == null) return null;
752 // If we are mocking, then return a mock label
753 if (RecentsDebugFlags.Static.EnableMockTasks) {
754 return new ColorDrawable(0xFF666666);
757 Drawable icon = info.loadIcon(mPm);
758 return getBadgedIcon(icon, userId);
762 * Returns the application icon for the ApplicationInfo for a user, badging if
765 public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
766 if (mPm == null) return null;
768 // If we are mocking, then return a mock label
769 if (RecentsDebugFlags.Static.EnableMockTasks) {
770 return new ColorDrawable(0xFF666666);
773 Drawable icon = appInfo.loadIcon(mPm);
774 return getBadgedIcon(icon, userId);
778 * Returns the task description icon, loading and badging it if it necessary.
780 public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
781 int userId, Resources res) {
783 // If we are mocking, then return a mock label
784 if (RecentsDebugFlags.Static.EnableMockTasks) {
785 return new ColorDrawable(0xFF666666);
788 Bitmap tdIcon = taskDescription.getInMemoryIcon();
789 if (tdIcon == null) {
790 tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
791 taskDescription.getIconFilename(), userId);
793 if (tdIcon != null) {
794 return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
800 * Returns the given icon for a user, badging if necessary.
802 private Drawable getBadgedIcon(Drawable icon, int userId) {
803 if (userId != UserHandle.myUserId()) {
804 icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
810 * Returns a banner used on TV for the specified Activity.
812 public Drawable getActivityBanner(ActivityInfo info) {
813 if (mPm == null) return null;
815 // If we are mocking, then return a mock banner
816 if (RecentsDebugFlags.Static.EnableMockTasks) {
817 return new ColorDrawable(0xFF666666);
820 Drawable banner = info.loadBanner(mPm);
825 * Returns a logo used on TV for the specified Activity.
827 public Drawable getActivityLogo(ActivityInfo info) {
828 if (mPm == null) return null;
830 // If we are mocking, then return a mock logo
831 if (RecentsDebugFlags.Static.EnableMockTasks) {
832 return new ColorDrawable(0xFF666666);
835 Drawable logo = info.loadLogo(mPm);
841 * Returns the given label for a user, badging if necessary.
843 private String getBadgedLabel(String label, int userId) {
844 if (userId != UserHandle.myUserId()) {
845 label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
850 /** Returns the package name of the home activity. */
851 public String getHomeActivityPackageName() {
852 if (mPm == null) return null;
853 if (RecentsDebugFlags.Static.EnableMockTasks) return null;
855 ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
856 ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
857 if (defaultHomeActivity != null) {
858 return defaultHomeActivity.getPackageName();
859 } else if (homeActivities.size() == 1) {
860 ResolveInfo info = homeActivities.get(0);
861 if (info.activityInfo != null) {
862 return info.activityInfo.packageName;
869 * Returns whether the provided {@param userId} represents the system user.
871 public boolean isSystemUser(int userId) {
872 return userId == UserHandle.USER_SYSTEM;
876 * Returns the current user id.
878 public int getCurrentUser() {
879 if (mAm == null) return 0;
881 return mAm.getCurrentUser();
885 * Returns the processes user id.
887 public int getProcessUser() {
888 if (mUm == null) return 0;
889 return mUm.getUserHandle();
893 * Returns whether touch exploration is currently enabled.
895 public boolean isTouchExplorationEnabled() {
896 if (mAccm == null) return false;
898 return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
902 * Returns whether the current task is in screen-pinning mode.
904 public boolean isScreenPinningActive() {
905 if (mIam == null) return false;
908 return mIam.isInLockTaskMode();
909 } catch (RemoteException e) {
915 * Returns a global setting.
917 public int getGlobalSetting(Context context, String setting) {
918 ContentResolver cr = context.getContentResolver();
919 return Settings.Global.getInt(cr, setting, 0);
923 * Returns a system setting.
925 public int getSystemSetting(Context context, String setting) {
926 ContentResolver cr = context.getContentResolver();
927 return Settings.System.getInt(cr, setting, 0);
931 * Returns a system property.
933 public String getSystemProperty(String key) {
934 return SystemProperties.get(key);
938 * Returns the smallest width/height.
940 public int getDeviceSmallestWidth() {
941 if (mDisplay == null) return 0;
943 Point smallestSizeRange = new Point();
944 Point largestSizeRange = new Point();
945 mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
946 return smallestSizeRange.x;
950 * Returns the current display rect in the current display orientation.
952 public Rect getDisplayRect() {
953 Rect displayRect = new Rect();
954 if (mDisplay == null) return displayRect;
956 Point p = new Point();
957 mDisplay.getRealSize(p);
958 displayRect.set(0, 0, p.x, p.y);
963 * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
965 public Rect getWindowRect() {
966 Rect windowRect = new Rect();
967 if (mIam == null) return windowRect;
970 // Use the home stack bounds
971 ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
972 if (stackInfo != null) {
973 windowRect.set(stackInfo.bounds);
975 } catch (RemoteException e) {
982 /** Starts an activity from recents. */
983 public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
984 ActivityOptions options) {
987 if (taskKey.stackId == DOCKED_STACK_ID) {
988 // We show non-visible docked tasks in Recents, but we always want to launch
989 // them in the fullscreen stack.
990 if (options == null) {
991 options = ActivityOptions.makeBasic();
993 options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
995 mIam.startActivityFromRecents(
996 taskKey.id, options == null ? null : options.toBundle());
998 } catch (Exception e) {
999 Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
1005 /** Starts an in-place animation on the front most application windows. */
1006 public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
1007 if (mIam == null) return;
1010 mIam.startInPlaceAnimationOnFrontMostApplication(opts);
1011 } catch (Exception e) {
1012 e.printStackTrace();
1017 * Registers a task stack listener with the system.
1018 * This should be called on the main thread.
1020 public void registerTaskStackListener(TaskStackListener listener) {
1021 if (mIam == null) return;
1023 mTaskStackListeners.add(listener);
1024 if (mTaskStackListeners.size() == 1) {
1025 // Register mTaskStackListener to IActivityManager only once if needed.
1027 mIam.registerTaskStackListener(mTaskStackListener);
1028 } catch (Exception e) {
1029 Log.w(TAG, "Failed to call registerTaskStackListener", e);
1034 public void endProlongedAnimations() {
1039 WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
1040 } catch (Exception e) {
1041 e.printStackTrace();
1045 public void registerDockedStackListener(IDockedStackListener listener) {
1046 if (mWm == null) return;
1049 WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
1050 } catch (Exception e) {
1051 e.printStackTrace();
1056 * Calculates the size of the dock divider in the current orientation.
1058 public int getDockedDividerSize(Context context) {
1059 Resources res = context.getResources();
1060 int dividerWindowWidth = res.getDimensionPixelSize(
1061 com.android.internal.R.dimen.docked_stack_divider_thickness);
1062 int dividerInsets = res.getDimensionPixelSize(
1063 com.android.internal.R.dimen.docked_stack_divider_insets);
1064 return dividerWindowWidth - 2 * dividerInsets;
1067 public void requestKeyboardShortcuts(
1068 Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
1069 mWm.requestAppKeyboardShortcuts(receiver, deviceId);
1072 public void getStableInsets(Rect outStableInsets) {
1073 if (mWm == null) return;
1076 WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets);
1077 } catch (Exception e) {
1078 e.printStackTrace();
1082 public void overridePendingAppTransitionMultiThumbFuture(
1083 IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
1086 WindowManagerGlobal.getWindowManagerService()
1087 .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
1089 } catch (RemoteException e) {
1090 Log.w(TAG, "Failed to override transition: " + e);
1094 private final class H extends Handler {
1095 private static final int ON_TASK_STACK_CHANGED = 1;
1096 private static final int ON_ACTIVITY_PINNED = 2;
1097 private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
1098 private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
1099 private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
1100 private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
1103 public void handleMessage(Message msg) {
1105 case ON_TASK_STACK_CHANGED: {
1106 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1107 mTaskStackListeners.get(i).onTaskStackChanged();
1111 case ON_ACTIVITY_PINNED: {
1112 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1113 mTaskStackListeners.get(i).onActivityPinned();
1117 case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
1118 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1119 mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
1123 case ON_PINNED_STACK_ANIMATION_ENDED: {
1124 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1125 mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
1129 case ON_ACTIVITY_FORCED_RESIZABLE: {
1130 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1131 mTaskStackListeners.get(i).onActivityForcedResizable(
1132 (String) msg.obj, msg.arg1);
1136 case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
1137 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1138 mTaskStackListeners.get(i).onActivityDismissingDockedStack();