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