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