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