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