• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.systemui.recents.misc;
18 
19 import android.app.ActivityManager;
20 import android.app.ActivityManagerNative;
21 import android.app.ActivityOptions;
22 import android.app.AppGlobals;
23 import android.app.IActivityManager;
24 import android.app.ITaskStackListener;
25 import android.appwidget.AppWidgetHost;
26 import android.appwidget.AppWidgetManager;
27 import android.appwidget.AppWidgetProviderInfo;
28 import android.content.ComponentName;
29 import android.content.ContentResolver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.IPackageManager;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Resources;
37 import android.graphics.Bitmap;
38 import android.graphics.BitmapFactory;
39 import android.graphics.Canvas;
40 import android.graphics.Color;
41 import android.graphics.Paint;
42 import android.graphics.Point;
43 import android.graphics.PorterDuff;
44 import android.graphics.PorterDuffXfermode;
45 import android.graphics.Rect;
46 import android.graphics.drawable.ColorDrawable;
47 import android.graphics.drawable.Drawable;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.HandlerThread;
51 import android.os.ParcelFileDescriptor;
52 import android.os.RemoteException;
53 import android.os.SystemProperties;
54 import android.os.UserHandle;
55 import android.provider.Settings;
56 import android.util.Log;
57 import android.util.MutableBoolean;
58 import android.util.Pair;
59 import android.util.SparseArray;
60 import android.view.Display;
61 import android.view.WindowManager;
62 import android.view.accessibility.AccessibilityManager;
63 
64 import com.android.internal.app.AssistUtils;
65 import com.android.systemui.Prefs;
66 import com.android.systemui.R;
67 import com.android.systemui.recents.Constants;
68 import com.android.systemui.recents.Recents;
69 
70 import java.io.IOException;
71 import java.util.ArrayList;
72 import java.util.Iterator;
73 import java.util.List;
74 import java.util.Random;
75 
76 /**
77  * Acts as a shim around the real system services that we need to access data from, and provides
78  * a point of injection when testing UI.
79  */
80 public class SystemServicesProxy {
81     final static String TAG = "SystemServicesProxy";
82 
83     final static BitmapFactory.Options sBitmapOptions;
84     final static HandlerThread sBgThread;
85 
86     static {
87         sBgThread = new HandlerThread("Recents-SystemServicesProxy",
88                 android.os.Process.THREAD_PRIORITY_BACKGROUND);
sBgThread.start()89         sBgThread.start();
90         sBitmapOptions = new BitmapFactory.Options();
91         sBitmapOptions.inMutable = true;
92     }
93 
94     AccessibilityManager mAccm;
95     ActivityManager mAm;
96     IActivityManager mIam;
97     AppWidgetManager mAwm;
98     PackageManager mPm;
99     IPackageManager mIpm;
100     AssistUtils mAssistUtils;
101     WindowManager mWm;
102     Display mDisplay;
103     String mRecentsPackage;
104     ComponentName mAssistComponent;
105 
106     Handler mBgThreadHandler;
107 
108     Bitmap mDummyIcon;
109     int mDummyThumbnailWidth;
110     int mDummyThumbnailHeight;
111     Paint mBgProtectionPaint;
112     Canvas mBgProtectionCanvas;
113 
114     /** Private constructor */
SystemServicesProxy(Context context)115     public SystemServicesProxy(Context context) {
116         mAccm = AccessibilityManager.getInstance(context);
117         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
118         mIam = ActivityManagerNative.getDefault();
119         mAwm = AppWidgetManager.getInstance(context);
120         mPm = context.getPackageManager();
121         mIpm = AppGlobals.getPackageManager();
122         mAssistUtils = new AssistUtils(context);
123         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
124         mDisplay = mWm.getDefaultDisplay();
125         mRecentsPackage = context.getPackageName();
126         mBgThreadHandler = new Handler(sBgThread.getLooper());
127 
128         // Get the dummy thumbnail width/heights
129         Resources res = context.getResources();
130         int wId = com.android.internal.R.dimen.thumbnail_width;
131         int hId = com.android.internal.R.dimen.thumbnail_height;
132         mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
133         mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
134 
135         // Create the protection paints
136         mBgProtectionPaint = new Paint();
137         mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
138         mBgProtectionPaint.setColor(0xFFffffff);
139         mBgProtectionCanvas = new Canvas();
140 
141         // Resolve the assist intent
142         mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
143 
144         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
145             // Create a dummy icon
146             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
147             mDummyIcon.eraseColor(0xFF999999);
148         }
149     }
150 
151     /** Returns a list of the recents tasks */
getRecentTasks(int numLatestTasks, int userId, boolean isTopTaskHome)152     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
153             boolean isTopTaskHome) {
154         if (mAm == null) return null;
155 
156         // If we are mocking, then create some recent tasks
157         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
158             ArrayList<ActivityManager.RecentTaskInfo> tasks =
159                     new ArrayList<ActivityManager.RecentTaskInfo>();
160             int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
161             for (int i = 0; i < count; i++) {
162                 // Create a dummy component name
163                 int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
164                 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
165                         "com.android.test" + i + ".Activity");
166                 String description = "" + i + " - " +
167                         Long.toString(Math.abs(new Random().nextLong()), 36);
168                 // Create the recent task info
169                 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
170                 rti.id = rti.persistentId = i;
171                 rti.baseIntent = new Intent();
172                 rti.baseIntent.setComponent(cn);
173                 rti.description = description;
174                 rti.firstActiveTime = rti.lastActiveTime = i;
175                 if (i % 2 == 0) {
176                     rti.taskDescription = new ActivityManager.TaskDescription(description,
177                         Bitmap.createBitmap(mDummyIcon),
178                         0xFF000000 | (0xFFFFFF & new Random().nextInt()));
179                 } else {
180                     rti.taskDescription = new ActivityManager.TaskDescription();
181                 }
182                 tasks.add(rti);
183             }
184             return tasks;
185         }
186 
187         // Remove home/recents/excluded tasks
188         int minNumTasksToQuery = 10;
189         int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
190         List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
191                 ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
192                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
193                 ActivityManager.RECENT_INCLUDE_PROFILES |
194                 ActivityManager.RECENT_WITH_EXCLUDED, userId);
195 
196         // Break early if we can't get a valid set of tasks
197         if (tasks == null) {
198             return new ArrayList<>();
199         }
200 
201         boolean isFirstValidTask = true;
202         Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
203         while (iter.hasNext()) {
204             ActivityManager.RecentTaskInfo t = iter.next();
205 
206             // NOTE: The order of these checks happens in the expected order of the traversal of the
207             // tasks
208 
209             // Check the first non-recents task, include this task even if it is marked as excluded
210             // from recents if we are currently in the app.  In other words, only remove excluded
211             // tasks if it is not the first active task.
212             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
213                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
214             if (isExcluded && (isTopTaskHome || !isFirstValidTask)) {
215                 iter.remove();
216                 continue;
217             }
218             isFirstValidTask = false;
219         }
220 
221         return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
222     }
223 
224     /** Returns a list of the running tasks */
getRunningTasks(int numTasks)225     private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
226         if (mAm == null) return null;
227         return mAm.getRunningTasks(numTasks);
228     }
229 
230     /** Returns the top task. */
getTopMostTask()231     public ActivityManager.RunningTaskInfo getTopMostTask() {
232         List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1);
233         if (tasks != null && !tasks.isEmpty()) {
234             return tasks.get(0);
235         }
236         return null;
237     }
238 
239     /** Returns whether the recents is currently running */
isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, MutableBoolean isHomeTopMost)240     public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
241             MutableBoolean isHomeTopMost) {
242         if (topTask != null) {
243             ComponentName topActivity = topTask.topActivity;
244 
245             // Check if the front most activity is recents
246             if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
247                     topActivity.getClassName().equals(Recents.sRecentsActivity)) {
248                 if (isHomeTopMost != null) {
249                     isHomeTopMost.value = false;
250                 }
251                 return true;
252             }
253 
254             if (isHomeTopMost != null) {
255                 isHomeTopMost.value = isInHomeStack(topTask.id);
256             }
257         }
258         return false;
259     }
260 
261     /** Get the bounds of a stack / task. */
getTaskBounds(int stackId)262     public Rect getTaskBounds(int stackId) {
263         ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
264         if (info != null)
265           return info.bounds;
266         return new Rect();
267     }
268 
269     /** Resize a given task. */
resizeTask(int taskId, Rect bounds)270     public void resizeTask(int taskId, Rect bounds) {
271         if (mIam == null) return;
272 
273         try {
274             mIam.resizeTask(taskId, bounds);
275         } catch (RemoteException e) {
276             e.printStackTrace();
277         }
278     }
279 
280     /** Returns the stack info for all stacks. */
getAllStackInfos()281     public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
282         if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
283 
284         try {
285             SparseArray<ActivityManager.StackInfo> stacks =
286                     new SparseArray<ActivityManager.StackInfo>();
287             List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
288             int stackCount = infos.size();
289             for (int i = 0; i < stackCount; i++) {
290                 ActivityManager.StackInfo info = infos.get(i);
291                 stacks.put(info.stackId, info);
292             }
293             return stacks;
294         } catch (RemoteException e) {
295             e.printStackTrace();
296             return new SparseArray<ActivityManager.StackInfo>();
297         }
298     }
299 
300     /** Returns the focused stack id. */
getFocusedStack()301     public int getFocusedStack() {
302         if (mIam == null) return -1;
303 
304         try {
305             return mIam.getFocusedStackId();
306         } catch (RemoteException e) {
307             e.printStackTrace();
308             return -1;
309         }
310     }
311 
312     /** Returns whether the specified task is in the home stack */
isInHomeStack(int taskId)313     public boolean isInHomeStack(int taskId) {
314         if (mAm == null) return false;
315 
316         // If we are mocking, then just return false
317         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
318             return false;
319         }
320 
321         return mAm.isInHomeStack(taskId);
322     }
323 
324     /** Returns the top task thumbnail for the given task id */
getTaskThumbnail(int taskId)325     public Bitmap getTaskThumbnail(int taskId) {
326         if (mAm == null) return null;
327 
328         // If we are mocking, then just return a dummy thumbnail
329         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
330             Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
331                     Bitmap.Config.ARGB_8888);
332             thumbnail.eraseColor(0xff333333);
333             return thumbnail;
334         }
335 
336         Bitmap thumbnail = SystemServicesProxy.getThumbnail(mAm, taskId);
337         if (thumbnail != null) {
338             thumbnail.setHasAlpha(false);
339             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
340             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
341             // screenshots are always composed onto a bitmap that has no alpha.
342             if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
343                 mBgProtectionCanvas.setBitmap(thumbnail);
344                 mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
345                         mBgProtectionPaint);
346                 mBgProtectionCanvas.setBitmap(null);
347                 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
348             }
349         }
350         return thumbnail;
351     }
352 
353     /**
354      * Returns a task thumbnail from the activity manager
355      */
getThumbnail(ActivityManager activityManager, int taskId)356     public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
357         ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
358         if (taskThumbnail == null) return null;
359 
360         Bitmap thumbnail = taskThumbnail.mainThumbnail;
361         ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
362         if (thumbnail == null && descriptor != null) {
363             thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
364                     null, sBitmapOptions);
365         }
366         if (descriptor != null) {
367             try {
368                 descriptor.close();
369             } catch (IOException e) {
370             }
371         }
372         return thumbnail;
373     }
374 
375     /** Moves a task to the front with the specified activity options. */
moveTaskToFront(int taskId, ActivityOptions opts)376     public void moveTaskToFront(int taskId, ActivityOptions opts) {
377         if (mAm == null) return;
378         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
379 
380         if (opts != null) {
381             mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME,
382                     opts.toBundle());
383         } else {
384             mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME);
385         }
386     }
387 
388     /** Removes the task */
removeTask(final int taskId)389     public void removeTask(final int taskId) {
390         if (mAm == null) return;
391         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
392 
393         // Remove the task.
394         mBgThreadHandler.post(new Runnable() {
395             @Override
396             public void run() {
397                 mAm.removeTask(taskId);
398             }
399         });
400     }
401 
402     /**
403      * Returns the activity info for a given component name.
404      *
405      * @param cn The component name of the activity.
406      * @param userId The userId of the user that this is for.
407      */
getActivityInfo(ComponentName cn, int userId)408     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
409         if (mIpm == null) return null;
410         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
411 
412         try {
413             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
414         } catch (RemoteException e) {
415             e.printStackTrace();
416             return null;
417         }
418     }
419 
420     /**
421      * Returns the activity info for a given component name.
422      *
423      * @param cn The component name of the activity.
424      */
getActivityInfo(ComponentName cn)425     public ActivityInfo getActivityInfo(ComponentName cn) {
426         if (mPm == null) return null;
427         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return new ActivityInfo();
428 
429         try {
430             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
431         } catch (PackageManager.NameNotFoundException e) {
432             e.printStackTrace();
433             return null;
434         }
435     }
436 
437     /** Returns the activity label */
getActivityLabel(ActivityInfo info)438     public String getActivityLabel(ActivityInfo info) {
439         if (mPm == null) return null;
440 
441         // If we are mocking, then return a mock label
442         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
443             return "Recent Task";
444         }
445 
446         return info.loadLabel(mPm).toString();
447     }
448 
449     /** Returns the application label */
getApplicationLabel(Intent baseIntent, int userId)450     public String getApplicationLabel(Intent baseIntent, int userId) {
451         if (mPm == null) return null;
452 
453         // If we are mocking, then return a mock label
454         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
455             return "Recent Task";
456         }
457 
458         ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId);
459         CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null;
460         return (label != null) ? label.toString() : null;
461     }
462 
463     /** Returns the content description for a given task */
getContentDescription(Intent baseIntent, int userId, String activityLabel, Resources res)464     public String getContentDescription(Intent baseIntent, int userId, String activityLabel,
465             Resources res) {
466         String applicationLabel = getApplicationLabel(baseIntent, userId);
467         if (applicationLabel == null) {
468             return getBadgedLabel(activityLabel, userId);
469         }
470         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
471         return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
472                 : res.getString(R.string.accessibility_recents_task_header,
473                         badgedApplicationLabel, activityLabel);
474     }
475 
476     /**
477      * Returns the activity icon for the ActivityInfo for a user, badging if
478      * necessary.
479      */
getActivityIcon(ActivityInfo info, int userId)480     public Drawable getActivityIcon(ActivityInfo info, int userId) {
481         if (mPm == null) return null;
482 
483         // If we are mocking, then return a mock label
484         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
485             return new ColorDrawable(0xFF666666);
486         }
487 
488         Drawable icon = info.loadIcon(mPm);
489         return getBadgedIcon(icon, userId);
490     }
491 
492     /**
493      * Returns the given icon for a user, badging if necessary.
494      */
getBadgedIcon(Drawable icon, int userId)495     public Drawable getBadgedIcon(Drawable icon, int userId) {
496         if (userId != UserHandle.myUserId()) {
497             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
498         }
499         return icon;
500     }
501 
502     /**
503      * Returns the given label for a user, badging if necessary.
504      */
getBadgedLabel(String label, int userId)505     public String getBadgedLabel(String label, int userId) {
506         if (userId != UserHandle.myUserId()) {
507             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
508         }
509         return label;
510     }
511 
512     /** Returns the package name of the home activity. */
getHomeActivityPackageName()513     public String getHomeActivityPackageName() {
514         if (mPm == null) return null;
515         if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null;
516 
517         ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
518         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
519         if (defaultHomeActivity != null) {
520             return defaultHomeActivity.getPackageName();
521         } else if (homeActivities.size() == 1) {
522             ResolveInfo info = homeActivities.get(0);
523             if (info.activityInfo != null) {
524                 return info.activityInfo.packageName;
525             }
526         }
527         return null;
528     }
529 
530     /**
531      * Returns whether the foreground user is the owner.
532      */
isForegroundUserOwner()533     public boolean isForegroundUserOwner() {
534         if (mAm == null) return false;
535 
536         return mAm.getCurrentUser() == UserHandle.USER_OWNER;
537     }
538 
539     /**
540      * Returns the current search widget id.
541      */
getSearchAppWidgetId(Context context)542     public int getSearchAppWidgetId(Context context) {
543         return Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
544     }
545 
546     /**
547      * Returns the current search widget info, binding a new one if necessary.
548      */
getOrBindSearchAppWidget(Context context, AppWidgetHost host)549     public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
550         int searchWidgetId = Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
551         AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
552         AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
553 
554         // Return the search widget info if it hasn't changed
555         if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
556                 searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
557             if (Prefs.getString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null) == null) {
558                 Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
559                         searchWidgetInfo.provider.getPackageName());
560             }
561             return searchWidgetInfo;
562         }
563 
564         // Delete the old widget
565         if (searchWidgetId != -1) {
566             host.deleteAppWidgetId(searchWidgetId);
567         }
568 
569         // And rebind a new search widget
570         if (resolvedSearchWidgetInfo != null) {
571             Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
572                     resolvedSearchWidgetInfo);
573             if (widgetInfo != null) {
574                 Prefs.putInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, widgetInfo.first);
575                 Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
576                         widgetInfo.second.provider.getPackageName());
577                 return widgetInfo.second;
578             }
579         }
580 
581         // If we fall through here, then there is no resolved search widget, so clear the state
582         Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_ID);
583         Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE);
584         return null;
585     }
586 
587     /**
588      * Returns the first Recents widget from the same package as the global assist activity.
589      */
resolveSearchAppWidget()590     private AppWidgetProviderInfo resolveSearchAppWidget() {
591         if (mAssistComponent == null) return null;
592         List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
593                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
594         for (AppWidgetProviderInfo info : widgets) {
595             if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) {
596                 return info;
597             }
598         }
599         return null;
600     }
601 
602     /**
603      * Resolves and binds the search app widget that is to appear in the recents.
604      */
bindSearchAppWidget(AppWidgetHost host, AppWidgetProviderInfo resolvedSearchWidgetInfo)605     private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host,
606             AppWidgetProviderInfo resolvedSearchWidgetInfo) {
607         if (mAwm == null) return null;
608         if (mAssistComponent == null) return null;
609 
610         // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
611         int searchWidgetId = host.allocateAppWidgetId();
612         Bundle opts = new Bundle();
613         opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
614                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
615         if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) {
616             host.deleteAppWidgetId(searchWidgetId);
617             return null;
618         }
619         return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo);
620     }
621 
622     /**
623      * Returns whether touch exploration is currently enabled.
624      */
isTouchExplorationEnabled()625     public boolean isTouchExplorationEnabled() {
626         if (mAccm == null) return false;
627 
628         return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
629     }
630 
631     /**
632      * Returns a global setting.
633      */
getGlobalSetting(Context context, String setting)634     public int getGlobalSetting(Context context, String setting) {
635         ContentResolver cr = context.getContentResolver();
636         return Settings.Global.getInt(cr, setting, 0);
637     }
638 
639     /**
640      * Returns a system setting.
641      */
getSystemSetting(Context context, String setting)642     public int getSystemSetting(Context context, String setting) {
643         ContentResolver cr = context.getContentResolver();
644         return Settings.System.getInt(cr, setting, 0);
645     }
646 
647     /**
648      * Returns a system property.
649      */
getSystemProperty(String key)650     public String getSystemProperty(String key) {
651         return SystemProperties.get(key);
652     }
653 
654     /**
655      * Returns the window rect.
656      */
getWindowRect()657     public Rect getWindowRect() {
658         Rect windowRect = new Rect();
659         if (mWm == null) return windowRect;
660 
661         Point p = new Point();
662         mWm.getDefaultDisplay().getRealSize(p);
663         windowRect.set(0, 0, p.x, p.y);
664         return windowRect;
665     }
666 
667     /** Starts an activity from recents. */
startActivityFromRecents(Context context, int taskId, String taskName, ActivityOptions options)668     public boolean startActivityFromRecents(Context context, int taskId, String taskName,
669             ActivityOptions options) {
670         if (mIam != null) {
671             try {
672                 mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
673                 return true;
674             } catch (Exception e) {
675                 Console.logError(context,
676                         context.getString(R.string.recents_launch_error_message, taskName));
677             }
678         }
679         return false;
680     }
681 
682     /** Starts an in-place animation on the front most application windows. */
startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)683     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
684         if (mIam == null) return;
685 
686         try {
687             mIam.startInPlaceAnimationOnFrontMostApplication(opts);
688         } catch (Exception e) {
689             e.printStackTrace();
690         }
691     }
692 
693     /** Registers a task stack listener with the system. */
registerTaskStackListener(ITaskStackListener listener)694     public void registerTaskStackListener(ITaskStackListener listener) {
695         if (mIam == null) return;
696 
697         try {
698             mIam.registerTaskStackListener(listener);
699         } catch (Exception e) {
700             e.printStackTrace();
701         }
702     }
703 }
704