SystemServicesProxy.java revision c92a7d12e345e05272f3e84d49d75c77dc6e3edc
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.ApplicationInfo; 34import android.content.pm.IPackageManager; 35import android.content.pm.PackageManager; 36import android.content.pm.ResolveInfo; 37import android.content.res.Resources; 38import android.graphics.Bitmap; 39import android.graphics.BitmapFactory; 40import android.graphics.Canvas; 41import android.graphics.Color; 42import android.graphics.Paint; 43import android.graphics.Point; 44import android.graphics.PorterDuff; 45import android.graphics.PorterDuffXfermode; 46import android.graphics.Rect; 47import android.graphics.drawable.BitmapDrawable; 48import android.graphics.drawable.ColorDrawable; 49import android.graphics.drawable.Drawable; 50import android.hardware.display.DisplayManager; 51import android.os.Bundle; 52import android.os.ParcelFileDescriptor; 53import android.os.RemoteException; 54import android.os.SystemProperties; 55import android.os.UserHandle; 56import android.os.UserManager; 57import android.provider.Settings; 58import android.util.ArraySet; 59import android.util.Log; 60import android.util.MutableBoolean; 61import android.util.Pair; 62import android.view.Display; 63import android.view.IDockedStackListener; 64import android.view.View; 65import android.view.WindowManager; 66import android.view.WindowManager.KeyboardShortcutsReceiver; 67import android.view.WindowManagerGlobal; 68import android.view.accessibility.AccessibilityManager; 69 70import com.android.internal.app.AssistUtils; 71import com.android.internal.os.BackgroundThread; 72import com.android.systemui.Prefs; 73import com.android.systemui.R; 74import com.android.systemui.recents.RecentsDebugFlags; 75import com.android.systemui.recents.RecentsImpl; 76 77import java.io.IOException; 78import java.util.ArrayList; 79import java.util.Iterator; 80import java.util.List; 81import java.util.Random; 82 83import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 84import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 85import static android.app.ActivityManager.StackId.HOME_STACK_ID; 86import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 87import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; 88 89/** 90 * Acts as a shim around the real system services that we need to access data from, and provides 91 * a point of injection when testing UI. 92 */ 93public class SystemServicesProxy { 94 final static String TAG = "SystemServicesProxy"; 95 96 final static BitmapFactory.Options sBitmapOptions; 97 98 static { 99 sBitmapOptions = new BitmapFactory.Options(); 100 sBitmapOptions.inMutable = true; 101 } 102 103 AccessibilityManager mAccm; 104 ActivityManager mAm; 105 IActivityManager mIam; 106 AppWidgetManager mAwm; 107 PackageManager mPm; 108 IPackageManager mIpm; 109 AssistUtils mAssistUtils; 110 WindowManager mWm; 111 UserManager mUm; 112 Display mDisplay; 113 String mRecentsPackage; 114 ComponentName mAssistComponent; 115 116 boolean mIsSafeMode; 117 boolean mHasFreeformWorkspaceSupport; 118 119 Bitmap mDummyIcon; 120 int mDummyThumbnailWidth; 121 int mDummyThumbnailHeight; 122 Paint mBgProtectionPaint; 123 Canvas mBgProtectionCanvas; 124 125 /** Private constructor */ 126 public SystemServicesProxy(Context context) { 127 mAccm = AccessibilityManager.getInstance(context); 128 mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 129 mIam = ActivityManagerNative.getDefault(); 130 mAwm = AppWidgetManager.getInstance(context); 131 mPm = context.getPackageManager(); 132 mIpm = AppGlobals.getPackageManager(); 133 mAssistUtils = new AssistUtils(context); 134 mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 135 mUm = UserManager.get(context); 136 mDisplay = mWm.getDefaultDisplay(); 137 mRecentsPackage = context.getPackageName(); 138 mHasFreeformWorkspaceSupport = 139 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) || 140 Settings.Global.getInt(context.getContentResolver(), 141 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 142 mIsSafeMode = mPm.isSafeMode(); 143 144 // Get the dummy thumbnail width/heights 145 Resources res = context.getResources(); 146 int wId = com.android.internal.R.dimen.thumbnail_width; 147 int hId = com.android.internal.R.dimen.thumbnail_height; 148 mDummyThumbnailWidth = res.getDimensionPixelSize(wId); 149 mDummyThumbnailHeight = res.getDimensionPixelSize(hId); 150 151 // Create the protection paints 152 mBgProtectionPaint = new Paint(); 153 mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); 154 mBgProtectionPaint.setColor(0xFFffffff); 155 mBgProtectionCanvas = new Canvas(); 156 157 // Resolve the assist intent 158 mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()); 159 160 if (RecentsDebugFlags.Static.EnableMockTasks) { 161 // Create a dummy icon 162 mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 163 mDummyIcon.eraseColor(0xFF999999); 164 } 165 } 166 167 /** Returns a list of the recents tasks */ 168 public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId, 169 boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) { 170 if (mAm == null) return null; 171 172 // If we are mocking, then create some recent tasks 173 if (RecentsDebugFlags.Static.EnableMockTasks) { 174 ArrayList<ActivityManager.RecentTaskInfo> tasks = 175 new ArrayList<ActivityManager.RecentTaskInfo>(); 176 int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount); 177 for (int i = 0; i < count; i++) { 178 // Create a dummy component name 179 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount; 180 ComponentName cn = new ComponentName("com.android.test" + packageIndex, 181 "com.android.test" + i + ".Activity"); 182 String description = "" + i + " - " + 183 Long.toString(Math.abs(new Random().nextLong()), 36); 184 // Create the recent task info 185 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); 186 rti.id = rti.persistentId = rti.affiliatedTaskId = i; 187 rti.baseIntent = new Intent(); 188 rti.baseIntent.setComponent(cn); 189 rti.description = description; 190 rti.firstActiveTime = rti.lastActiveTime = i; 191 if (i % 2 == 0) { 192 rti.taskDescription = new ActivityManager.TaskDescription(description, 193 Bitmap.createBitmap(mDummyIcon), null, 194 0xFF000000 | (0xFFFFFF & new Random().nextInt()), 195 0xFF000000 | (0xFFFFFF & new Random().nextInt())); 196 } else { 197 rti.taskDescription = new ActivityManager.TaskDescription(); 198 } 199 tasks.add(rti); 200 } 201 return tasks; 202 } 203 204 // Remove home/recents/excluded tasks 205 int minNumTasksToQuery = 10; 206 int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); 207 List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, 208 ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | 209 ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS | 210 ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS | 211 ActivityManager.RECENT_IGNORE_UNAVAILABLE | 212 ActivityManager.RECENT_INCLUDE_PROFILES | 213 ActivityManager.RECENT_WITH_EXCLUDED, userId); 214 215 // Break early if we can't get a valid set of tasks 216 if (tasks == null) { 217 return new ArrayList<>(); 218 } 219 220 boolean isFirstValidTask = true; 221 Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); 222 while (iter.hasNext()) { 223 ActivityManager.RecentTaskInfo t = iter.next(); 224 225 // NOTE: The order of these checks happens in the expected order of the traversal of the 226 // tasks 227 228 // Check the first non-recents task, include this task even if it is marked as excluded 229 // from recents if we are currently in the app. In other words, only remove excluded 230 // tasks if it is not the first active task. 231 boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 232 == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 233 // Filter out recent tasks from managed profiles which are in quiet mode. 234 isExcluded |= quietProfileIds.contains(t.userId); 235 if (isExcluded && (isTopTaskHome || !isFirstValidTask)) { 236 iter.remove(); 237 continue; 238 } 239 isFirstValidTask = false; 240 } 241 242 return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); 243 } 244 245 /** Returns a list of the running tasks */ 246 private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) { 247 if (mAm == null) return null; 248 return mAm.getRunningTasks(numTasks); 249 } 250 251 /** Returns the top task. */ 252 public ActivityManager.RunningTaskInfo getTopMostTask() { 253 List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1); 254 if (tasks != null && !tasks.isEmpty()) { 255 return tasks.get(0); 256 } 257 return null; 258 } 259 260 /** 261 * Returns whether this device has freeform workspaces. 262 */ 263 public boolean hasFreeformWorkspaceSupport() { 264 return mHasFreeformWorkspaceSupport; 265 } 266 267 /** 268 * Returns whether this device is in the safe mode. 269 */ 270 public boolean isInSafeMode() { 271 return mIsSafeMode; 272 } 273 274 /** Returns whether the recents is currently running */ 275 public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, 276 MutableBoolean isHomeTopMost) { 277 if (topTask != null) { 278 ComponentName topActivity = topTask.topActivity; 279 280 // Check if the front most activity is recents 281 if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) && 282 (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) || 283 topActivity.getClassName().equals(RecentsImpl.RECENTS_TV_ACTIVITY)))) { 284 if (isHomeTopMost != null) { 285 isHomeTopMost.value = false; 286 } 287 return true; 288 } 289 290 // Note, this is only valid because we currently only allow the recents and home 291 // activities in the home stack 292 if (isHomeTopMost != null) { 293 isHomeTopMost.value = SystemServicesProxy.isHomeStack(topTask.stackId); 294 } 295 } 296 return false; 297 } 298 299 /** Get the bounds of a task. */ 300 public Rect getTaskBounds(int taskId) { 301 if (mIam == null) return null; 302 303 try { 304 return mIam.getTaskBounds(taskId); 305 } catch (RemoteException e) { 306 e.printStackTrace(); 307 } 308 return null; 309 } 310 311 /** 312 * Resizes the given task to the new bounds. 313 */ 314 public void resizeTask(int taskId, Rect bounds) { 315 if (mIam == null) return; 316 317 try { 318 mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED); 319 } catch (RemoteException e) { 320 e.printStackTrace(); 321 } 322 } 323 324 /** Docks a task to the side of the screen and starts it. */ 325 public void startTaskInDockedMode(Context context, View view, int taskId, int createMode) { 326 if (mIam == null) return; 327 328 try { 329 // TODO: Determine what animation we want for the incoming task 330 final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation( 331 view, null, 0, 0, view.getWidth(), view.getHeight(), null, null); 332 options.setDockCreateMode(createMode); 333 options.setLaunchStackId(DOCKED_STACK_ID); 334 mIam.startActivityFromRecents(taskId, options.toBundle()); 335 } catch (RemoteException e) { 336 e.printStackTrace(); 337 } 338 } 339 340 /** Docks an already resumed task to the side of the screen. */ 341 public void moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) { 342 if (mIam == null) return; 343 344 try { 345 mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */, false /* animate */, 346 initialBounds); 347 } catch (RemoteException e) { 348 e.printStackTrace(); 349 } 350 } 351 352 /** Returns the focused stack id. */ 353 public int getFocusedStack() { 354 if (mIam == null) return -1; 355 356 try { 357 return mIam.getFocusedStackId(); 358 } catch (RemoteException e) { 359 e.printStackTrace(); 360 return -1; 361 } 362 } 363 364 /** 365 * Returns whether the given stack id is the home stack id. 366 */ 367 public static boolean isHomeStack(int stackId) { 368 return stackId == HOME_STACK_ID; 369 } 370 371 /** 372 * Returns whether the given stack id is the pinned stack id. 373 */ 374 public static boolean isPinnedStack(int stackId){ 375 return stackId == PINNED_STACK_ID; 376 } 377 378 /** 379 * Returns whether the given stack id is the docked stack id. 380 */ 381 public static boolean isDockedStack(int stackId) { 382 return stackId == DOCKED_STACK_ID; 383 } 384 385 /** 386 * Returns whether the given stack id is the freeform workspace stack id. 387 */ 388 public static boolean isFreeformStack(int stackId) { 389 return stackId == FREEFORM_WORKSPACE_STACK_ID; 390 } 391 392 /** 393 * @return whether there are any docked tasks for the current user. 394 */ 395 public boolean hasDockedTask() { 396 if (mIam == null) return false; 397 398 ActivityManager.StackInfo stackInfo = null; 399 try { 400 stackInfo = mIam.getStackInfo(DOCKED_STACK_ID); 401 } catch (RemoteException e) { 402 e.printStackTrace(); 403 } 404 405 if (stackInfo != null) { 406 int userId = getCurrentUser(); 407 boolean hasUserTask = false; 408 for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) { 409 hasUserTask = (stackInfo.taskUserIds[i] == userId); 410 } 411 return hasUserTask; 412 } 413 return false; 414 } 415 416 /** 417 * Cancels the current window transtion to/from Recents for the given task id. 418 */ 419 public void cancelWindowTransition(int taskId) { 420 if (mWm == null) return; 421 422 try { 423 WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId); 424 } catch (RemoteException e) { 425 e.printStackTrace(); 426 } 427 } 428 429 /** 430 * Cancels the current thumbnail transtion to/from Recents for the given task id. 431 */ 432 public void cancelThumbnailTransition(int taskId) { 433 if (mWm == null) return; 434 435 try { 436 WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId); 437 } catch (RemoteException e) { 438 e.printStackTrace(); 439 } 440 } 441 442 /** Returns the top task thumbnail for the given task id */ 443 public Bitmap getTaskThumbnail(int taskId) { 444 if (mAm == null) return null; 445 446 // If we are mocking, then just return a dummy thumbnail 447 if (RecentsDebugFlags.Static.EnableMockTasks) { 448 Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight, 449 Bitmap.Config.ARGB_8888); 450 thumbnail.eraseColor(0xff333333); 451 return thumbnail; 452 } 453 454 Bitmap thumbnail = getThumbnail(taskId); 455 if (thumbnail != null) { 456 thumbnail.setHasAlpha(false); 457 // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top 458 // left pixel, then assume the whole thumbnail is transparent. Generally, proper 459 // screenshots are always composed onto a bitmap that has no alpha. 460 if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) { 461 mBgProtectionCanvas.setBitmap(thumbnail); 462 mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(), 463 mBgProtectionPaint); 464 mBgProtectionCanvas.setBitmap(null); 465 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()"); 466 } 467 } 468 return thumbnail; 469 } 470 471 /** 472 * Returns a task thumbnail from the activity manager 473 */ 474 public Bitmap getThumbnail(int taskId) { 475 if (mAm == null) { 476 return null; 477 } 478 479 ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId); 480 if (taskThumbnail == null) return null; 481 482 Bitmap thumbnail = taskThumbnail.mainThumbnail; 483 ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; 484 if (thumbnail == null && descriptor != null) { 485 thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(), 486 null, sBitmapOptions); 487 } 488 if (descriptor != null) { 489 try { 490 descriptor.close(); 491 } catch (IOException e) { 492 } 493 } 494 return thumbnail; 495 } 496 497 /** 498 * Moves a task into another stack. 499 */ 500 public void moveTaskToStack(int taskId, int stackId) { 501 if (mIam == null) return; 502 503 try { 504 mIam.positionTaskInStack(taskId, stackId, 0); 505 } catch (RemoteException | IllegalArgumentException e) { 506 e.printStackTrace(); 507 } 508 } 509 510 /** Moves a task to the front with the specified activity options. */ 511 public void moveTaskToFront(int taskId, ActivityOptions opts) { 512 if (mAm == null) return; 513 if (RecentsDebugFlags.Static.EnableMockTasks) return; 514 515 if (opts != null) { 516 mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME, 517 opts.toBundle()); 518 } else { 519 mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME); 520 } 521 } 522 523 /** Removes the task */ 524 public void removeTask(final int taskId) { 525 if (mAm == null) return; 526 if (RecentsDebugFlags.Static.EnableMockTasks) return; 527 528 // Remove the task. 529 BackgroundThread.getHandler().post(new Runnable() { 530 @Override 531 public void run() { 532 mAm.removeTask(taskId); 533 } 534 }); 535 } 536 537 /** 538 * Sends a message to close other system windows. 539 */ 540 public void sendCloseSystemWindows(String reason) { 541 if (ActivityManagerNative.isSystemReady()) { 542 try { 543 mIam.closeSystemDialogs(reason); 544 } catch (RemoteException e) { 545 } 546 } 547 } 548 549 /** 550 * Returns the activity info for a given component name. 551 * 552 * @param cn The component name of the activity. 553 * @param userId The userId of the user that this is for. 554 */ 555 public ActivityInfo getActivityInfo(ComponentName cn, int userId) { 556 if (mIpm == null) return null; 557 if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo(); 558 559 try { 560 return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId); 561 } catch (RemoteException e) { 562 e.printStackTrace(); 563 return null; 564 } 565 } 566 567 /** 568 * Returns the activity info for a given component name. 569 * 570 * @param cn The component name of the activity. 571 */ 572 public ActivityInfo getActivityInfo(ComponentName cn) { 573 if (mPm == null) return null; 574 if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo(); 575 576 try { 577 return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA); 578 } catch (PackageManager.NameNotFoundException e) { 579 e.printStackTrace(); 580 return null; 581 } 582 } 583 584 /** 585 * Returns the activity label, badging if necessary. 586 */ 587 public String getBadgedActivityLabel(ActivityInfo info, int userId) { 588 if (mPm == null) return null; 589 590 // If we are mocking, then return a mock label 591 if (RecentsDebugFlags.Static.EnableMockTasks) { 592 return "Recent Task: " + userId; 593 } 594 595 return getBadgedLabel(info.loadLabel(mPm).toString(), userId); 596 } 597 598 /** 599 * Returns the application label, badging if necessary. 600 */ 601 public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) { 602 if (mPm == null) return null; 603 604 // If we are mocking, then return a mock label 605 if (RecentsDebugFlags.Static.EnableMockTasks) { 606 return "Recent Task App: " + userId; 607 } 608 609 return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId); 610 } 611 612 /** 613 * Returns the content description for a given task, badging it if necessary. The content 614 * description joins the app and activity labels. 615 */ 616 public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) { 617 // If we are mocking, then return a mock label 618 if (RecentsDebugFlags.Static.EnableMockTasks) { 619 return "Recent Task Content Description: " + userId; 620 } 621 622 String activityLabel = info.loadLabel(mPm).toString(); 623 String applicationLabel = info.applicationInfo.loadLabel(mPm).toString(); 624 String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); 625 return applicationLabel.equals(activityLabel) ? badgedApplicationLabel 626 : res.getString(R.string.accessibility_recents_task_header, 627 badgedApplicationLabel, activityLabel); 628 } 629 630 /** 631 * Returns the activity icon for the ActivityInfo for a user, badging if 632 * necessary. 633 */ 634 public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) { 635 if (mPm == null) return null; 636 637 // If we are mocking, then return a mock label 638 if (RecentsDebugFlags.Static.EnableMockTasks) { 639 return new ColorDrawable(0xFF666666); 640 } 641 642 Drawable icon = info.loadIcon(mPm); 643 return getBadgedIcon(icon, userId); 644 } 645 646 /** 647 * Returns the application icon for the ApplicationInfo for a user, badging if 648 * necessary. 649 */ 650 public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) { 651 if (mPm == null) return null; 652 653 // If we are mocking, then return a mock label 654 if (RecentsDebugFlags.Static.EnableMockTasks) { 655 return new ColorDrawable(0xFF666666); 656 } 657 658 Drawable icon = appInfo.loadIcon(mPm); 659 return getBadgedIcon(icon, userId); 660 } 661 662 /** 663 * Returns the task description icon, loading and badging it if it necessary. 664 */ 665 public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription, 666 int userId, Resources res) { 667 668 // If we are mocking, then return a mock label 669 if (RecentsDebugFlags.Static.EnableMockTasks) { 670 return new ColorDrawable(0xFF666666); 671 } 672 673 Bitmap tdIcon = taskDescription.getInMemoryIcon(); 674 if (tdIcon == null) { 675 tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon( 676 taskDescription.getIconFilename(), userId); 677 } 678 if (tdIcon != null) { 679 return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId); 680 } 681 return null; 682 } 683 684 /** 685 * Returns the given icon for a user, badging if necessary. 686 */ 687 private Drawable getBadgedIcon(Drawable icon, int userId) { 688 if (userId != UserHandle.myUserId()) { 689 icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId)); 690 } 691 return icon; 692 } 693 694 /** 695 * Returns the given label for a user, badging if necessary. 696 */ 697 private String getBadgedLabel(String label, int userId) { 698 if (userId != UserHandle.myUserId()) { 699 label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString(); 700 } 701 return label; 702 } 703 704 /** Returns the package name of the home activity. */ 705 public String getHomeActivityPackageName() { 706 if (mPm == null) return null; 707 if (RecentsDebugFlags.Static.EnableMockTasks) return null; 708 709 ArrayList<ResolveInfo> homeActivities = new ArrayList<>(); 710 ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities); 711 if (defaultHomeActivity != null) { 712 return defaultHomeActivity.getPackageName(); 713 } else if (homeActivities.size() == 1) { 714 ResolveInfo info = homeActivities.get(0); 715 if (info.activityInfo != null) { 716 return info.activityInfo.packageName; 717 } 718 } 719 return null; 720 } 721 722 /** 723 * Returns whether the provided {@param userId} represents the system user. 724 */ 725 public boolean isSystemUser(int userId) { 726 return userId == UserHandle.USER_SYSTEM; 727 } 728 729 /** 730 * Returns the current user id. 731 */ 732 public int getCurrentUser() { 733 if (mAm == null) return 0; 734 735 return mAm.getCurrentUser(); 736 } 737 738 /** 739 * Returns the processes user id. 740 */ 741 public int getProcessUser() { 742 if (mUm == null) return 0; 743 return mUm.getUserHandle(); 744 } 745 746 /** 747 * Returns the current search widget id. 748 */ 749 public int getSearchAppWidgetId(Context context) { 750 return Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1); 751 } 752 753 /** 754 * Returns the current search widget info, binding a new one if necessary. 755 */ 756 public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) { 757 int searchWidgetId = Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1); 758 AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId); 759 AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget(); 760 761 // Return the search widget info if it hasn't changed 762 if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null && 763 searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) { 764 if (Prefs.getString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null) == null) { 765 Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, 766 searchWidgetInfo.provider.getPackageName()); 767 } 768 return searchWidgetInfo; 769 } 770 771 // Delete the old widget 772 if (searchWidgetId != -1) { 773 host.deleteAppWidgetId(searchWidgetId); 774 } 775 776 // And rebind a new search widget 777 if (resolvedSearchWidgetInfo != null) { 778 Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host, 779 resolvedSearchWidgetInfo); 780 if (widgetInfo != null) { 781 Prefs.putInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, widgetInfo.first); 782 Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, 783 widgetInfo.second.provider.getPackageName()); 784 return widgetInfo.second; 785 } 786 } 787 788 // If we fall through here, then there is no resolved search widget, so clear the state 789 Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID); 790 Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE); 791 return null; 792 } 793 794 /** 795 * Returns the first Recents widget from the same package as the global assist activity. 796 */ 797 public AppWidgetProviderInfo resolveSearchAppWidget() { 798 if (mAssistComponent == null) return null; 799 List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders( 800 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); 801 for (AppWidgetProviderInfo info : widgets) { 802 if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) { 803 return info; 804 } 805 } 806 return null; 807 } 808 809 /** 810 * Resolves and binds the search app widget that is to appear in the recents. 811 */ 812 private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host, 813 AppWidgetProviderInfo resolvedSearchWidgetInfo) { 814 if (mAwm == null) return null; 815 if (mAssistComponent == null) return null; 816 817 // Allocate a new widget id and try and bind the app widget (if that fails, then just skip) 818 int searchWidgetId = host.allocateAppWidgetId(); 819 Bundle opts = new Bundle(); 820 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 821 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); 822 if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) { 823 host.deleteAppWidgetId(searchWidgetId); 824 return null; 825 } 826 return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo); 827 } 828 829 /** 830 * Returns whether touch exploration is currently enabled. 831 */ 832 public boolean isTouchExplorationEnabled() { 833 if (mAccm == null) return false; 834 835 return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled(); 836 } 837 838 /** 839 * Returns whether the current task is in screen-pinning mode. 840 */ 841 public boolean isScreenPinningActive() { 842 if (mIam == null) return false; 843 844 try { 845 return mIam.isInLockTaskMode(); 846 } catch (RemoteException e) { 847 return false; 848 } 849 } 850 851 /** 852 * Returns a global setting. 853 */ 854 public int getGlobalSetting(Context context, String setting) { 855 ContentResolver cr = context.getContentResolver(); 856 return Settings.Global.getInt(cr, setting, 0); 857 } 858 859 /** 860 * Returns a system setting. 861 */ 862 public int getSystemSetting(Context context, String setting) { 863 ContentResolver cr = context.getContentResolver(); 864 return Settings.System.getInt(cr, setting, 0); 865 } 866 867 /** 868 * Returns a system property. 869 */ 870 public String getSystemProperty(String key) { 871 return SystemProperties.get(key); 872 } 873 874 /** 875 * Returns the smallest width/height. 876 */ 877 public int getDeviceSmallestWidth() { 878 if (mWm == null) return 0; 879 880 Point smallestSizeRange = new Point(); 881 Point largestSizeRange = new Point(); 882 mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange); 883 return smallestSizeRange.x; 884 } 885 886 /** 887 * Returns the display rect. 888 */ 889 public Rect getDisplayRect() { 890 Rect displayRect = new Rect(); 891 if (mWm == null) return displayRect; 892 893 Point p = new Point(); 894 mWm.getDefaultDisplay().getRealSize(p); 895 displayRect.set(0, 0, p.x, p.y); 896 return displayRect; 897 } 898 899 /** 900 * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack. 901 */ 902 public Rect getWindowRect() { 903 Rect windowRect = new Rect(); 904 if (mIam == null) return windowRect; 905 906 try { 907 // Use the home stack bounds 908 ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID); 909 if (stackInfo != null) { 910 windowRect.set(stackInfo.bounds); 911 } 912 } catch (RemoteException e) { 913 e.printStackTrace(); 914 } finally { 915 return windowRect; 916 } 917 } 918 919 /** Starts an activity from recents. */ 920 public boolean startActivityFromRecents(Context context, int taskId, String taskName, 921 ActivityOptions options) { 922 if (mIam != null) { 923 try { 924 mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle()); 925 return true; 926 } catch (Exception e) { 927 Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); 928 } 929 } 930 return false; 931 } 932 933 /** Starts an in-place animation on the front most application windows. */ 934 public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) { 935 if (mIam == null) return; 936 937 try { 938 mIam.startInPlaceAnimationOnFrontMostApplication(opts); 939 } catch (Exception e) { 940 e.printStackTrace(); 941 } 942 } 943 944 /** Registers a task stack listener with the system. */ 945 public void registerTaskStackListener(ITaskStackListener listener) { 946 if (mIam == null) return; 947 948 try { 949 mIam.registerTaskStackListener(listener); 950 } catch (Exception e) { 951 e.printStackTrace(); 952 } 953 } 954 955 public void endProlongedAnimations() { 956 if (mWm == null) { 957 return; 958 } 959 try { 960 WindowManagerGlobal.getWindowManagerService().endProlongedAnimations(); 961 } catch (Exception e) { 962 e.printStackTrace(); 963 } 964 } 965 966 public void registerDockedStackListener(IDockedStackListener listener) { 967 if (mWm == null) return; 968 969 try { 970 WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener); 971 } catch (Exception e) { 972 e.printStackTrace(); 973 } 974 } 975 976 /** 977 * Calculates the size of the dock divider in the current orientation. 978 */ 979 public int getDockedDividerSize(Context context) { 980 Resources res = context.getResources(); 981 int dividerWindowWidth = res.getDimensionPixelSize( 982 com.android.internal.R.dimen.docked_stack_divider_thickness); 983 int dividerInsets = res.getDimensionPixelSize( 984 com.android.internal.R.dimen.docked_stack_divider_insets); 985 return dividerWindowWidth - 2 * dividerInsets; 986 } 987 988 public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) { 989 mWm.requestAppKeyboardShortcuts(receiver); 990 } 991 992 public void getStableInsets(Rect outStableInsets) { 993 if (mWm == null) return; 994 995 try { 996 WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets); 997 } catch (Exception e) { 998 e.printStackTrace(); 999 } 1000 } 1001} 1002