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