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