SystemServicesProxy.java revision c92a7d12e345e05272f3e84d49d75c77dc6e3edc
1/*
2 * Copyright (C) 2014 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.recents.misc;
18
19import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
21import android.app.ActivityOptions;
22import android.app.AppGlobals;
23import android.app.IActivityManager;
24import android.app.ITaskStackListener;
25import android.appwidget.AppWidgetHost;
26import android.appwidget.AppWidgetManager;
27import android.appwidget.AppWidgetProviderInfo;
28import android.content.ComponentName;
29import android.content.ContentResolver;
30import android.content.Context;
31import android.content.Intent;
32import android.content.pm.ActivityInfo;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
35import android.content.pm.PackageManager;
36import android.content.pm.ResolveInfo;
37import android.content.res.Resources;
38import android.graphics.Bitmap;
39import android.graphics.BitmapFactory;
40import android.graphics.Canvas;
41import android.graphics.Color;
42import android.graphics.Paint;
43import android.graphics.Point;
44import android.graphics.PorterDuff;
45import android.graphics.PorterDuffXfermode;
46import android.graphics.Rect;
47import android.graphics.drawable.BitmapDrawable;
48import android.graphics.drawable.ColorDrawable;
49import android.graphics.drawable.Drawable;
50import android.hardware.display.DisplayManager;
51import android.os.Bundle;
52import android.os.ParcelFileDescriptor;
53import android.os.RemoteException;
54import android.os.SystemProperties;
55import android.os.UserHandle;
56import android.os.UserManager;
57import android.provider.Settings;
58import android.util.ArraySet;
59import android.util.Log;
60import android.util.MutableBoolean;
61import android.util.Pair;
62import android.view.Display;
63import android.view.IDockedStackListener;
64import android.view.View;
65import android.view.WindowManager;
66import android.view.WindowManager.KeyboardShortcutsReceiver;
67import android.view.WindowManagerGlobal;
68import android.view.accessibility.AccessibilityManager;
69
70import com.android.internal.app.AssistUtils;
71import com.android.internal.os.BackgroundThread;
72import com.android.systemui.Prefs;
73import com.android.systemui.R;
74import com.android.systemui.recents.RecentsDebugFlags;
75import com.android.systemui.recents.RecentsImpl;
76
77import java.io.IOException;
78import java.util.ArrayList;
79import java.util.Iterator;
80import java.util.List;
81import java.util.Random;
82
83import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
84import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
85import static android.app.ActivityManager.StackId.HOME_STACK_ID;
86import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
87import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
88
89/**
90 * Acts as a shim around the real system services that we need to access data from, and provides
91 * a point of injection when testing UI.
92 */
93public class SystemServicesProxy {
94    final static String TAG = "SystemServicesProxy";
95
96    final static BitmapFactory.Options sBitmapOptions;
97
98    static {
99        sBitmapOptions = new BitmapFactory.Options();
100        sBitmapOptions.inMutable = true;
101    }
102
103    AccessibilityManager mAccm;
104    ActivityManager mAm;
105    IActivityManager mIam;
106    AppWidgetManager mAwm;
107    PackageManager mPm;
108    IPackageManager mIpm;
109    AssistUtils mAssistUtils;
110    WindowManager mWm;
111    UserManager mUm;
112    Display mDisplay;
113    String mRecentsPackage;
114    ComponentName mAssistComponent;
115
116    boolean mIsSafeMode;
117    boolean mHasFreeformWorkspaceSupport;
118
119    Bitmap mDummyIcon;
120    int mDummyThumbnailWidth;
121    int mDummyThumbnailHeight;
122    Paint mBgProtectionPaint;
123    Canvas mBgProtectionCanvas;
124
125    /** Private constructor */
126    public SystemServicesProxy(Context context) {
127        mAccm = AccessibilityManager.getInstance(context);
128        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
129        mIam = ActivityManagerNative.getDefault();
130        mAwm = AppWidgetManager.getInstance(context);
131        mPm = context.getPackageManager();
132        mIpm = AppGlobals.getPackageManager();
133        mAssistUtils = new AssistUtils(context);
134        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
135        mUm = UserManager.get(context);
136        mDisplay = mWm.getDefaultDisplay();
137        mRecentsPackage = context.getPackageName();
138        mHasFreeformWorkspaceSupport =
139                mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
140                        Settings.Global.getInt(context.getContentResolver(),
141                                DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
142        mIsSafeMode = mPm.isSafeMode();
143
144        // Get the dummy thumbnail width/heights
145        Resources res = context.getResources();
146        int wId = com.android.internal.R.dimen.thumbnail_width;
147        int hId = com.android.internal.R.dimen.thumbnail_height;
148        mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
149        mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
150
151        // Create the protection paints
152        mBgProtectionPaint = new Paint();
153        mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
154        mBgProtectionPaint.setColor(0xFFffffff);
155        mBgProtectionCanvas = new Canvas();
156
157        // Resolve the assist intent
158        mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
159
160        if (RecentsDebugFlags.Static.EnableMockTasks) {
161            // Create a dummy icon
162            mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
163            mDummyIcon.eraseColor(0xFF999999);
164        }
165    }
166
167    /** Returns a list of the recents tasks */
168    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
169            boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) {
170        if (mAm == null) return null;
171
172        // If we are mocking, then create some recent tasks
173        if (RecentsDebugFlags.Static.EnableMockTasks) {
174            ArrayList<ActivityManager.RecentTaskInfo> tasks =
175                    new ArrayList<ActivityManager.RecentTaskInfo>();
176            int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
177            for (int i = 0; i < count; i++) {
178                // Create a dummy component name
179                int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
180                ComponentName cn = new ComponentName("com.android.test" + packageIndex,
181                        "com.android.test" + i + ".Activity");
182                String description = "" + i + " - " +
183                        Long.toString(Math.abs(new Random().nextLong()), 36);
184                // Create the recent task info
185                ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
186                rti.id = rti.persistentId = rti.affiliatedTaskId = i;
187                rti.baseIntent = new Intent();
188                rti.baseIntent.setComponent(cn);
189                rti.description = description;
190                rti.firstActiveTime = rti.lastActiveTime = i;
191                if (i % 2 == 0) {
192                    rti.taskDescription = new ActivityManager.TaskDescription(description,
193                        Bitmap.createBitmap(mDummyIcon), null,
194                        0xFF000000 | (0xFFFFFF & new Random().nextInt()),
195                        0xFF000000 | (0xFFFFFF & new Random().nextInt()));
196                } else {
197                    rti.taskDescription = new ActivityManager.TaskDescription();
198                }
199                tasks.add(rti);
200            }
201            return tasks;
202        }
203
204        // Remove home/recents/excluded tasks
205        int minNumTasksToQuery = 10;
206        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
207        List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
208                ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
209                ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS |
210                ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
211                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
212                ActivityManager.RECENT_INCLUDE_PROFILES |
213                ActivityManager.RECENT_WITH_EXCLUDED, userId);
214
215        // Break early if we can't get a valid set of tasks
216        if (tasks == null) {
217            return new ArrayList<>();
218        }
219
220        boolean isFirstValidTask = true;
221        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
222        while (iter.hasNext()) {
223            ActivityManager.RecentTaskInfo t = iter.next();
224
225            // NOTE: The order of these checks happens in the expected order of the traversal of the
226            // tasks
227
228            // Check the first non-recents task, include this task even if it is marked as excluded
229            // from recents if we are currently in the app.  In other words, only remove excluded
230            // tasks if it is not the first active task.
231            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
232                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
233            // Filter out recent tasks from managed profiles which are in quiet mode.
234            isExcluded |= quietProfileIds.contains(t.userId);
235            if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
236                iter.remove();
237                continue;
238            }
239            isFirstValidTask = false;
240        }
241
242        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
243    }
244
245    /** Returns a list of the running tasks */
246    private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
247        if (mAm == null) return null;
248        return mAm.getRunningTasks(numTasks);
249    }
250
251    /** Returns the top task. */
252    public ActivityManager.RunningTaskInfo getTopMostTask() {
253        List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);
254        if (tasks != null && !tasks.isEmpty()) {
255            return tasks.get(0);
256        }
257        return null;
258    }
259
260    /**
261     * Returns whether this device has freeform workspaces.
262     */
263    public boolean hasFreeformWorkspaceSupport() {
264        return mHasFreeformWorkspaceSupport;
265    }
266
267    /**
268     * Returns whether this device is in the safe mode.
269     */
270    public boolean isInSafeMode() {
271        return mIsSafeMode;
272    }
273
274    /** Returns whether the recents is currently running */
275    public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
276            MutableBoolean isHomeTopMost) {
277        if (topTask != null) {
278            ComponentName topActivity = topTask.topActivity;
279
280            // Check if the front most activity is recents
281            if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
282                    (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) ||
283                    topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) {
284                if (isHomeTopMost != null) {
285                    isHomeTopMost.value = false;
286                }
287                return true;
288            }
289
290            // Note, this is only valid because we currently only allow the recents and home
291            // activities in the home stack
292            if (isHomeTopMost != null) {
293                isHomeTopMost.value = SystemServicesProxy.isHomeStack(topTask.stackId);
294            }
295        }
296        return false;
297    }
298
299    /** Get the bounds of a task. */
300    public Rect getTaskBounds(int taskId) {
301        if (mIam == null) return null;
302
303        try {
304            return mIam.getTaskBounds(taskId);
305        } catch (RemoteException e) {
306            e.printStackTrace();
307        }
308        return null;
309    }
310
311    /**
312     * Resizes the given task to the new bounds.
313     */
314    public void resizeTask(int taskId, Rect bounds) {
315        if (mIam == null) return;
316
317        try {
318            mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED);
319        } catch (RemoteException e) {
320            e.printStackTrace();
321        }
322    }
323
324    /** Docks a task to the side of the screen and starts it. */
325    public void startTaskInDockedMode(Context context, View view, int taskId, int createMode) {
326        if (mIam == null) return;
327
328        try {
329            // TODO: Determine what animation we want for the incoming task
330            final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation(
331                    view, null, 0, 0, view.getWidth(), view.getHeight(), null, null);
332            options.setDockCreateMode(createMode);
333            options.setLaunchStackId(DOCKED_STACK_ID);
334            mIam.startActivityFromRecents(taskId, options.toBundle());
335        } catch (RemoteException e) {
336            e.printStackTrace();
337        }
338    }
339
340    /** Docks an already resumed task to the side of the screen. */
341    public void moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
342        if (mIam == null) return;
343
344        try {
345            mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */, false /* animate */,
346                    initialBounds);
347        } catch (RemoteException e) {
348            e.printStackTrace();
349        }
350    }
351
352    /** Returns the focused stack id. */
353    public int getFocusedStack() {
354        if (mIam == null) return -1;
355
356        try {
357            return mIam.getFocusedStackId();
358        } catch (RemoteException e) {
359            e.printStackTrace();
360            return -1;
361        }
362    }
363
364    /**
365     * Returns whether the given stack id is the home stack id.
366     */
367    public static boolean isHomeStack(int stackId) {
368        return stackId == HOME_STACK_ID;
369    }
370
371    /**
372     * Returns whether the given stack id is the pinned stack id.
373     */
374    public static boolean isPinnedStack(int stackId){
375        return stackId == PINNED_STACK_ID;
376    }
377
378    /**
379     * Returns whether the given stack id is the docked stack id.
380     */
381    public static boolean isDockedStack(int stackId) {
382        return stackId == DOCKED_STACK_ID;
383    }
384
385    /**
386     * Returns whether the given stack id is the freeform workspace stack id.
387     */
388    public static boolean isFreeformStack(int stackId) {
389        return stackId == FREEFORM_WORKSPACE_STACK_ID;
390    }
391
392    /**
393     * @return whether there are any docked tasks for the current user.
394     */
395    public boolean hasDockedTask() {
396        if (mIam == null) return false;
397
398        ActivityManager.StackInfo stackInfo = null;
399        try {
400            stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
401        } catch (RemoteException e) {
402            e.printStackTrace();
403        }
404
405        if (stackInfo != null) {
406            int userId = getCurrentUser();
407            boolean hasUserTask = false;
408            for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
409                hasUserTask = (stackInfo.taskUserIds[i] == userId);
410            }
411            return hasUserTask;
412        }
413        return false;
414    }
415
416    /**
417     * Cancels the current window transtion to/from Recents for the given task id.
418     */
419    public void cancelWindowTransition(int taskId) {
420        if (mWm == null) return;
421
422        try {
423            WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId);
424        } catch (RemoteException e) {
425            e.printStackTrace();
426        }
427    }
428
429    /**
430     * Cancels the current thumbnail transtion to/from Recents for the given task id.
431     */
432    public void cancelThumbnailTransition(int taskId) {
433        if (mWm == null) return;
434
435        try {
436            WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId);
437        } catch (RemoteException e) {
438            e.printStackTrace();
439        }
440    }
441
442    /** Returns the top task thumbnail for the given task id */
443    public Bitmap getTaskThumbnail(int taskId) {
444        if (mAm == null) return null;
445
446        // If we are mocking, then just return a dummy thumbnail
447        if (RecentsDebugFlags.Static.EnableMockTasks) {
448            Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
449                    Bitmap.Config.ARGB_8888);
450            thumbnail.eraseColor(0xff333333);
451            return thumbnail;
452        }
453
454        Bitmap thumbnail = getThumbnail(taskId);
455        if (thumbnail != null) {
456            thumbnail.setHasAlpha(false);
457            // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
458            // left pixel, then assume the whole thumbnail is transparent. Generally, proper
459            // screenshots are always composed onto a bitmap that has no alpha.
460            if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
461                mBgProtectionCanvas.setBitmap(thumbnail);
462                mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
463                        mBgProtectionPaint);
464                mBgProtectionCanvas.setBitmap(null);
465                Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
466            }
467        }
468        return thumbnail;
469    }
470
471    /**
472     * Returns a task thumbnail from the activity manager
473     */
474    public Bitmap getThumbnail(int taskId) {
475        if (mAm == null) {
476            return null;
477        }
478
479        ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
480        if (taskThumbnail == null) return null;
481
482        Bitmap thumbnail = taskThumbnail.mainThumbnail;
483        ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
484        if (thumbnail == null && descriptor != null) {
485            thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
486                    null, sBitmapOptions);
487        }
488        if (descriptor != null) {
489            try {
490                descriptor.close();
491            } catch (IOException e) {
492            }
493        }
494        return thumbnail;
495    }
496
497    /**
498     * Moves a task into another stack.
499     */
500    public void moveTaskToStack(int taskId, int stackId) {
501        if (mIam == null) return;
502
503        try {
504            mIam.positionTaskInStack(taskId, stackId, 0);
505        } catch (RemoteException | IllegalArgumentException e) {
506            e.printStackTrace();
507        }
508    }
509
510    /** Moves a task to the front with the specified activity options. */
511    public void moveTaskToFront(int taskId, ActivityOptions opts) {
512        if (mAm == null) return;
513        if (RecentsDebugFlags.Static.EnableMockTasks) return;
514
515        if (opts != null) {
516            mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
517                    opts.toBundle());
518        } else {
519            mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME);
520        }
521    }
522
523    /** Removes the task */
524    public void removeTask(final int taskId) {
525        if (mAm == null) return;
526        if (RecentsDebugFlags.Static.EnableMockTasks) return;
527
528        // Remove the task.
529        BackgroundThread.getHandler().post(new Runnable() {
530            @Override
531            public void run() {
532                mAm.removeTask(taskId);
533            }
534        });
535    }
536
537    /**
538     * Sends a message to close other system windows.
539     */
540    public void sendCloseSystemWindows(String reason) {
541        if (ActivityManagerNative.isSystemReady()) {
542            try {
543                mIam.closeSystemDialogs(reason);
544            } catch (RemoteException e) {
545            }
546        }
547    }
548
549    /**
550     * Returns the activity info for a given component name.
551     *
552     * @param cn The component name of the activity.
553     * @param userId The userId of the user that this is for.
554     */
555    public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
556        if (mIpm == null) return null;
557        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
558
559        try {
560            return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
561        } catch (RemoteException e) {
562            e.printStackTrace();
563            return null;
564        }
565    }
566
567    /**
568     * Returns the activity info for a given component name.
569     *
570     * @param cn The component name of the activity.
571     */
572    public ActivityInfo getActivityInfo(ComponentName cn) {
573        if (mPm == null) return null;
574        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
575
576        try {
577            return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
578        } catch (PackageManager.NameNotFoundException e) {
579            e.printStackTrace();
580            return null;
581        }
582    }
583
584    /**
585     * Returns the activity label, badging if necessary.
586     */
587    public String getBadgedActivityLabel(ActivityInfo info, int userId) {
588        if (mPm == null) return null;
589
590        // If we are mocking, then return a mock label
591        if (RecentsDebugFlags.Static.EnableMockTasks) {
592            return "Recent Task: " + userId;
593        }
594
595        return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
596    }
597
598    /**
599     * Returns the application label, badging if necessary.
600     */
601    public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
602        if (mPm == null) return null;
603
604        // If we are mocking, then return a mock label
605        if (RecentsDebugFlags.Static.EnableMockTasks) {
606            return "Recent Task App: " + userId;
607        }
608
609        return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
610    }
611
612    /**
613     * Returns the content description for a given task, badging it if necessary.  The content
614     * description joins the app and activity labels.
615     */
616    public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
617        // If we are mocking, then return a mock label
618        if (RecentsDebugFlags.Static.EnableMockTasks) {
619            return "Recent Task Content Description: " + userId;
620        }
621
622        String activityLabel = info.loadLabel(mPm).toString();
623        String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
624        String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
625        return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
626                : res.getString(R.string.accessibility_recents_task_header,
627                        badgedApplicationLabel, activityLabel);
628    }
629
630    /**
631     * Returns the activity icon for the ActivityInfo for a user, badging if
632     * necessary.
633     */
634    public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
635        if (mPm == null) return null;
636
637        // If we are mocking, then return a mock label
638        if (RecentsDebugFlags.Static.EnableMockTasks) {
639            return new ColorDrawable(0xFF666666);
640        }
641
642        Drawable icon = info.loadIcon(mPm);
643        return getBadgedIcon(icon, userId);
644    }
645
646    /**
647     * Returns the application icon for the ApplicationInfo for a user, badging if
648     * necessary.
649     */
650    public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
651        if (mPm == null) return null;
652
653        // If we are mocking, then return a mock label
654        if (RecentsDebugFlags.Static.EnableMockTasks) {
655            return new ColorDrawable(0xFF666666);
656        }
657
658        Drawable icon = appInfo.loadIcon(mPm);
659        return getBadgedIcon(icon, userId);
660    }
661
662    /**
663     * Returns the task description icon, loading and badging it if it necessary.
664     */
665    public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
666            int userId, Resources res) {
667
668        // If we are mocking, then return a mock label
669        if (RecentsDebugFlags.Static.EnableMockTasks) {
670            return new ColorDrawable(0xFF666666);
671        }
672
673        Bitmap tdIcon = taskDescription.getInMemoryIcon();
674        if (tdIcon == null) {
675            tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
676                    taskDescription.getIconFilename(), userId);
677        }
678        if (tdIcon != null) {
679            return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
680        }
681        return null;
682    }
683
684    /**
685     * Returns the given icon for a user, badging if necessary.
686     */
687    private Drawable getBadgedIcon(Drawable icon, int userId) {
688        if (userId != UserHandle.myUserId()) {
689            icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
690        }
691        return icon;
692    }
693
694    /**
695     * Returns the given label for a user, badging if necessary.
696     */
697    private String getBadgedLabel(String label, int userId) {
698        if (userId != UserHandle.myUserId()) {
699            label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
700        }
701        return label;
702    }
703
704    /** Returns the package name of the home activity. */
705    public String getHomeActivityPackageName() {
706        if (mPm == null) return null;
707        if (RecentsDebugFlags.Static.EnableMockTasks) return null;
708
709        ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
710        ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
711        if (defaultHomeActivity != null) {
712            return defaultHomeActivity.getPackageName();
713        } else if (homeActivities.size() == 1) {
714            ResolveInfo info = homeActivities.get(0);
715            if (info.activityInfo != null) {
716                return info.activityInfo.packageName;
717            }
718        }
719        return null;
720    }
721
722    /**
723     * Returns whether the provided {@param userId} represents the system user.
724     */
725    public boolean isSystemUser(int userId) {
726        return userId == UserHandle.USER_SYSTEM;
727    }
728
729    /**
730     * Returns the current user id.
731     */
732    public int getCurrentUser() {
733        if (mAm == null) return 0;
734
735        return mAm.getCurrentUser();
736    }
737
738    /**
739     * Returns the processes user id.
740     */
741    public int getProcessUser() {
742        if (mUm == null) return 0;
743        return mUm.getUserHandle();
744    }
745
746    /**
747     * Returns the current search widget id.
748     */
749    public int getSearchAppWidgetId(Context context) {
750        return Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
751    }
752
753    /**
754     * Returns the current search widget info, binding a new one if necessary.
755     */
756    public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
757        int searchWidgetId = Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1);
758        AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
759        AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
760
761        // Return the search widget info if it hasn't changed
762        if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
763                searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
764            if (Prefs.getString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null) == null) {
765                Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
766                        searchWidgetInfo.provider.getPackageName());
767            }
768            return searchWidgetInfo;
769        }
770
771        // Delete the old widget
772        if (searchWidgetId != -1) {
773            host.deleteAppWidgetId(searchWidgetId);
774        }
775
776        // And rebind a new search widget
777        if (resolvedSearchWidgetInfo != null) {
778            Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
779                    resolvedSearchWidgetInfo);
780            if (widgetInfo != null) {
781                Prefs.putInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, widgetInfo.first);
782                Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE,
783                        widgetInfo.second.provider.getPackageName());
784                return widgetInfo.second;
785            }
786        }
787
788        // If we fall through here, then there is no resolved search widget, so clear the state
789        Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID);
790        Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE);
791        return null;
792    }
793
794    /**
795     * Returns the first Recents widget from the same package as the global assist activity.
796     */
797    public AppWidgetProviderInfo resolveSearchAppWidget() {
798        if (mAssistComponent == null) return null;
799        List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
800                AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
801        for (AppWidgetProviderInfo info : widgets) {
802            if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) {
803                return info;
804            }
805        }
806        return null;
807    }
808
809    /**
810     * Resolves and binds the search app widget that is to appear in the recents.
811     */
812    private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host,
813            AppWidgetProviderInfo resolvedSearchWidgetInfo) {
814        if (mAwm == null) return null;
815        if (mAssistComponent == null) return null;
816
817        // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
818        int searchWidgetId = host.allocateAppWidgetId();
819        Bundle opts = new Bundle();
820        opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
821                AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
822        if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) {
823            host.deleteAppWidgetId(searchWidgetId);
824            return null;
825        }
826        return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo);
827    }
828
829    /**
830     * Returns whether touch exploration is currently enabled.
831     */
832    public boolean isTouchExplorationEnabled() {
833        if (mAccm == null) return false;
834
835        return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
836    }
837
838    /**
839     * Returns whether the current task is in screen-pinning mode.
840     */
841    public boolean isScreenPinningActive() {
842        if (mIam == null) return false;
843
844        try {
845            return mIam.isInLockTaskMode();
846        } catch (RemoteException e) {
847            return false;
848        }
849    }
850
851    /**
852     * Returns a global setting.
853     */
854    public int getGlobalSetting(Context context, String setting) {
855        ContentResolver cr = context.getContentResolver();
856        return Settings.Global.getInt(cr, setting, 0);
857    }
858
859    /**
860     * Returns a system setting.
861     */
862    public int getSystemSetting(Context context, String setting) {
863        ContentResolver cr = context.getContentResolver();
864        return Settings.System.getInt(cr, setting, 0);
865    }
866
867    /**
868     * Returns a system property.
869     */
870    public String getSystemProperty(String key) {
871        return SystemProperties.get(key);
872    }
873
874    /**
875     * Returns the smallest width/height.
876     */
877    public int getDeviceSmallestWidth() {
878        if (mWm == null) return 0;
879
880        Point smallestSizeRange = new Point();
881        Point largestSizeRange = new Point();
882        mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange);
883        return smallestSizeRange.x;
884    }
885
886    /**
887     * Returns the display rect.
888     */
889    public Rect getDisplayRect() {
890        Rect displayRect = new Rect();
891        if (mWm == null) return displayRect;
892
893        Point p = new Point();
894        mWm.getDefaultDisplay().getRealSize(p);
895        displayRect.set(0, 0, p.x, p.y);
896        return displayRect;
897    }
898
899    /**
900     * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
901     */
902    public Rect getWindowRect() {
903        Rect windowRect = new Rect();
904        if (mIam == null) return windowRect;
905
906        try {
907            // Use the home stack bounds
908            ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
909            if (stackInfo != null) {
910                windowRect.set(stackInfo.bounds);
911            }
912        } catch (RemoteException e) {
913            e.printStackTrace();
914        } finally {
915            return windowRect;
916        }
917    }
918
919    /** Starts an activity from recents. */
920    public boolean startActivityFromRecents(Context context, int taskId, String taskName,
921            ActivityOptions options) {
922        if (mIam != null) {
923            try {
924                mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
925                return true;
926            } catch (Exception e) {
927                Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
928            }
929        }
930        return false;
931    }
932
933    /** Starts an in-place animation on the front most application windows. */
934    public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
935        if (mIam == null) return;
936
937        try {
938            mIam.startInPlaceAnimationOnFrontMostApplication(opts);
939        } catch (Exception e) {
940            e.printStackTrace();
941        }
942    }
943
944    /** Registers a task stack listener with the system. */
945    public void registerTaskStackListener(ITaskStackListener listener) {
946        if (mIam == null) return;
947
948        try {
949            mIam.registerTaskStackListener(listener);
950        } catch (Exception e) {
951            e.printStackTrace();
952        }
953    }
954
955    public void endProlongedAnimations() {
956        if (mWm == null) {
957            return;
958        }
959        try {
960            WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
961        } catch (Exception e) {
962            e.printStackTrace();
963        }
964    }
965
966    public void registerDockedStackListener(IDockedStackListener listener) {
967        if (mWm == null) return;
968
969        try {
970            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
971        } catch (Exception e) {
972            e.printStackTrace();
973        }
974    }
975
976    /**
977     * Calculates the size of the dock divider in the current orientation.
978     */
979    public int getDockedDividerSize(Context context) {
980        Resources res = context.getResources();
981        int dividerWindowWidth = res.getDimensionPixelSize(
982                com.android.internal.R.dimen.docked_stack_divider_thickness);
983        int dividerInsets = res.getDimensionPixelSize(
984                com.android.internal.R.dimen.docked_stack_divider_insets);
985        return dividerWindowWidth - 2 * dividerInsets;
986    }
987
988    public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
989        mWm.requestAppKeyboardShortcuts(receiver);
990    }
991
992    public void getStableInsets(Rect outStableInsets) {
993        if (mWm == null) return;
994
995        try {
996            WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets);
997        } catch (Exception e) {
998            e.printStackTrace();
999        }
1000    }
1001}
1002