RecentsView.java revision af3bb6936786a8c14ac01da3d81d196d17b68b96
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.views; 18 19import android.app.ActivityOptions; 20import android.app.TaskStackBuilder; 21import android.content.Context; 22import android.content.Intent; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Rect; 26import android.net.Uri; 27import android.os.Bundle; 28import android.os.IRemoteCallback; 29import android.os.RemoteException; 30import android.os.UserHandle; 31import android.provider.Settings; 32import android.util.AttributeSet; 33import android.util.Log; 34import android.view.LayoutInflater; 35import android.view.View; 36import android.view.WindowInsets; 37import android.view.WindowManagerGlobal; 38import android.widget.FrameLayout; 39 40import com.android.systemui.R; 41import com.android.systemui.recents.Constants; 42import com.android.systemui.recents.RecentsAppWidgetHostView; 43import com.android.systemui.recents.RecentsConfiguration; 44import com.android.systemui.recents.misc.SystemServicesProxy; 45import com.android.systemui.recents.model.RecentsPackageMonitor; 46import com.android.systemui.recents.model.RecentsTaskLoader; 47import com.android.systemui.recents.model.Task; 48import com.android.systemui.recents.model.TaskStack; 49 50import java.util.ArrayList; 51import java.util.List; 52 53/** 54 * This view is the the top level layout that contains TaskStacks (which are laid out according 55 * to their SpaceNode bounds. 56 */ 57public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks, 58 RecentsPackageMonitor.PackageCallbacks { 59 60 private static final String TAG = "RecentsView"; 61 62 /** The RecentsView callbacks */ 63 public interface RecentsViewCallbacks { 64 public void onTaskViewClicked(); 65 public void onTaskLaunchFailed(); 66 public void onAllTaskViewsDismissed(); 67 public void onExitToHomeAnimationTriggered(); 68 public void onScreenPinningRequest(); 69 public void onTaskResize(Task t); 70 public void runAfterPause(Runnable r); 71 } 72 73 RecentsConfiguration mConfig; 74 LayoutInflater mInflater; 75 DebugOverlayView mDebugOverlay; 76 RecentsViewLayoutAlgorithm mLayoutAlgorithm; 77 78 ArrayList<TaskStack> mStacks; 79 List<TaskStackView> mTaskStackViews = new ArrayList<>(); 80 RecentsAppWidgetHostView mSearchBar; 81 RecentsViewCallbacks mCb; 82 83 public RecentsView(Context context) { 84 super(context); 85 } 86 87 public RecentsView(Context context, AttributeSet attrs) { 88 this(context, attrs, 0); 89 } 90 91 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { 92 this(context, attrs, defStyleAttr, 0); 93 } 94 95 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 96 super(context, attrs, defStyleAttr, defStyleRes); 97 mConfig = RecentsConfiguration.getInstance(); 98 mInflater = LayoutInflater.from(context); 99 mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig); 100 } 101 102 /** Sets the callbacks */ 103 public void setCallbacks(RecentsViewCallbacks cb) { 104 mCb = cb; 105 } 106 107 /** Sets the debug overlay */ 108 public void setDebugOverlay(DebugOverlayView overlay) { 109 mDebugOverlay = overlay; 110 } 111 112 /** Set/get the bsp root node */ 113 public void setTaskStacks(ArrayList<TaskStack> stacks) { 114 int numStacks = stacks.size(); 115 116 // Remove all/extra stack views 117 int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout 118 if (mConfig.launchedReuseTaskStackViews) { 119 numTaskStacksToKeep = Math.min(mTaskStackViews.size(), numStacks); 120 } 121 for (int i = mTaskStackViews.size() - 1; i >= numTaskStacksToKeep; i--) { 122 removeView(mTaskStackViews.remove(i)); 123 } 124 125 // Update the stack views that we are keeping 126 for (int i = 0; i < numTaskStacksToKeep; i++) { 127 TaskStackView tsv = mTaskStackViews.get(i); 128 // If onRecentsHidden is not triggered, we need to the stack view again here 129 tsv.reset(); 130 tsv.setStack(stacks.get(i)); 131 } 132 133 // Add remaining/recreate stack views 134 mStacks = stacks; 135 for (int i = mTaskStackViews.size(); i < numStacks; i++) { 136 TaskStack stack = stacks.get(i); 137 TaskStackView stackView = new TaskStackView(getContext(), stack); 138 stackView.setCallbacks(this); 139 addView(stackView); 140 mTaskStackViews.add(stackView); 141 } 142 143 // Enable debug mode drawing on all the stacks if necessary 144 if (mConfig.debugModeEnabled) { 145 for (int i = mTaskStackViews.size() - 1; i >= 0; i--) { 146 TaskStackView stackView = mTaskStackViews.get(i); 147 stackView.setDebugOverlay(mDebugOverlay); 148 } 149 } 150 151 // Trigger a new layout 152 requestLayout(); 153 } 154 155 /** Gets the list of task views */ 156 List<TaskStackView> getTaskStackViews() { 157 return mTaskStackViews; 158 } 159 160 /** Gets the next task in the stack - or if the last - the top task */ 161 public Task getNextTaskOrTopTask(Task taskToSearch) { 162 Task returnTask = null; 163 boolean found = false; 164 List<TaskStackView> stackViews = getTaskStackViews(); 165 int stackCount = stackViews.size(); 166 for (int i = stackCount - 1; i >= 0; --i) { 167 TaskStack stack = stackViews.get(i).getStack(); 168 ArrayList<Task> taskList = stack.getTasks(); 169 // Iterate the stack views and try and find the focused task 170 for (int j = taskList.size() - 1; j >= 0; --j) { 171 Task task = taskList.get(j); 172 // Return the next task in the line. 173 if (found) 174 return task; 175 // Remember the first possible task as the top task. 176 if (returnTask == null) 177 returnTask = task; 178 if (task == taskToSearch) 179 found = true; 180 } 181 } 182 return returnTask; 183 } 184 185 /** Launches the focused task from the first stack if possible */ 186 public boolean launchFocusedTask() { 187 // Get the first stack view 188 List<TaskStackView> stackViews = getTaskStackViews(); 189 int stackCount = stackViews.size(); 190 for (int i = 0; i < stackCount; i++) { 191 TaskStackView stackView = stackViews.get(i); 192 TaskStack stack = stackView.getStack(); 193 // Iterate the stack views and try and find the focused task 194 List<TaskView> taskViews = stackView.getTaskViews(); 195 int taskViewCount = taskViews.size(); 196 for (int j = 0; j < taskViewCount; j++) { 197 TaskView tv = taskViews.get(j); 198 Task task = tv.getTask(); 199 if (tv.isFocusedTask()) { 200 onTaskViewClicked(stackView, tv, stack, task, false); 201 return true; 202 } 203 } 204 } 205 return false; 206 } 207 208 /** Launches a given task. */ 209 public boolean launchTask(Task task) { 210 // Get the first stack view 211 List<TaskStackView> stackViews = getTaskStackViews(); 212 int stackCount = stackViews.size(); 213 for (int i = 0; i < stackCount; i++) { 214 TaskStackView stackView = stackViews.get(i); 215 TaskStack stack = stackView.getStack(); 216 // Iterate the stack views and try and find the given task. 217 List<TaskView> taskViews = stackView.getTaskViews(); 218 int taskViewCount = taskViews.size(); 219 for (int j = 0; j < taskViewCount; j++) { 220 TaskView tv = taskViews.get(j); 221 if (tv.getTask() == task) { 222 onTaskViewClicked(stackView, tv, stack, task, false); 223 return true; 224 } 225 } 226 } 227 return false; 228 } 229 230 /** Launches the task that Recents was launched from, if possible */ 231 public boolean launchPreviousTask() { 232 // Get the first stack view 233 List<TaskStackView> stackViews = getTaskStackViews(); 234 int stackCount = stackViews.size(); 235 for (int i = 0; i < stackCount; i++) { 236 TaskStackView stackView = stackViews.get(i); 237 TaskStack stack = stackView.getStack(); 238 ArrayList<Task> tasks = stack.getTasks(); 239 240 // Find the launch task in the stack 241 if (!tasks.isEmpty()) { 242 int taskCount = tasks.size(); 243 for (int j = 0; j < taskCount; j++) { 244 if (tasks.get(j).isLaunchTarget) { 245 Task task = tasks.get(j); 246 TaskView tv = stackView.getChildViewForTask(task); 247 onTaskViewClicked(stackView, tv, stack, task, false); 248 return true; 249 } 250 } 251 } 252 } 253 return false; 254 } 255 256 /** Requests all task stacks to start their enter-recents animation */ 257 public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) { 258 // We have to increment/decrement the post animation trigger in case there are no children 259 // to ensure that it runs 260 ctx.postAnimationTrigger.increment(); 261 262 List<TaskStackView> stackViews = getTaskStackViews(); 263 int stackCount = stackViews.size(); 264 for (int i = 0; i < stackCount; i++) { 265 TaskStackView stackView = stackViews.get(i); 266 stackView.startEnterRecentsAnimation(ctx); 267 } 268 ctx.postAnimationTrigger.decrement(); 269 } 270 271 /** Requests all task stacks to start their exit-recents animation */ 272 public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) { 273 // We have to increment/decrement the post animation trigger in case there are no children 274 // to ensure that it runs 275 ctx.postAnimationTrigger.increment(); 276 List<TaskStackView> stackViews = getTaskStackViews(); 277 int stackCount = stackViews.size(); 278 for (int i = 0; i < stackCount; i++) { 279 TaskStackView stackView = stackViews.get(i); 280 stackView.startExitToHomeAnimation(ctx); 281 } 282 ctx.postAnimationTrigger.decrement(); 283 284 // Notify of the exit animation 285 mCb.onExitToHomeAnimationTriggered(); 286 } 287 288 /** Adds the search bar */ 289 public void setSearchBar(RecentsAppWidgetHostView searchBar) { 290 // Remove the previous search bar if one exists 291 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) { 292 removeView(mSearchBar); 293 } 294 // Add the new search bar 295 if (searchBar != null) { 296 mSearchBar = searchBar; 297 addView(mSearchBar); 298 } 299 } 300 301 /** Returns whether there is currently a search bar */ 302 public boolean hasValidSearchBar() { 303 return mSearchBar != null && !mSearchBar.isReinflateRequired(); 304 } 305 306 /** Sets the visibility of the search bar */ 307 public void setSearchBarVisibility(int visibility) { 308 if (mSearchBar != null) { 309 mSearchBar.setVisibility(visibility); 310 // Always bring the search bar to the top 311 mSearchBar.bringToFront(); 312 } 313 } 314 315 /** 316 * This is called with the full size of the window since we are handling our own insets. 317 */ 318 @Override 319 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 320 int width = MeasureSpec.getSize(widthMeasureSpec); 321 int height = MeasureSpec.getSize(heightMeasureSpec); 322 323 // Get the search bar bounds and measure the search bar layout 324 Rect searchBarSpaceBounds = new Rect(); 325 if (mSearchBar != null) { 326 mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds); 327 mSearchBar.measure( 328 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY), 329 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY)); 330 } 331 332 Rect taskStackBounds = new Rect(); 333 mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top, 334 mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds); 335 336 // Measure each TaskStackView with the full width and height of the window since the 337 // transition view is a child of that stack view 338 List<TaskStackView> stackViews = getTaskStackViews(); 339 List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews, 340 taskStackBounds); 341 int stackCount = stackViews.size(); 342 for (int i = 0; i < stackCount; i++) { 343 TaskStackView stackView = stackViews.get(i); 344 if (stackView.getVisibility() != GONE) { 345 // We are going to measure the TaskStackView with the whole RecentsView dimensions, 346 // but the actual stack is going to be inset to the bounds calculated by the layout 347 // algorithm 348 stackView.setStackInsetRect(stackViewsBounds.get(i)); 349 stackView.measure(widthMeasureSpec, heightMeasureSpec); 350 } 351 } 352 353 setMeasuredDimension(width, height); 354 } 355 356 /** 357 * This is called with the full size of the window since we are handling our own insets. 358 */ 359 @Override 360 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 361 // Get the search bar bounds so that we lay it out 362 if (mSearchBar != null) { 363 Rect searchBarSpaceBounds = new Rect(); 364 mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), 365 mConfig.systemInsets.top, searchBarSpaceBounds); 366 mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top, 367 searchBarSpaceBounds.right, searchBarSpaceBounds.bottom); 368 } 369 370 // Layout each TaskStackView with the full width and height of the window since the 371 // transition view is a child of that stack view 372 List<TaskStackView> stackViews = getTaskStackViews(); 373 int stackCount = stackViews.size(); 374 for (int i = 0; i < stackCount; i++) { 375 TaskStackView stackView = stackViews.get(i); 376 if (stackView.getVisibility() != GONE) { 377 stackView.layout(left, top, left + stackView.getMeasuredWidth(), 378 top + stackView.getMeasuredHeight()); 379 } 380 } 381 } 382 383 @Override 384 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 385 // Update the configuration with the latest system insets and trigger a relayout 386 mConfig.updateSystemInsets(insets.getSystemWindowInsets()); 387 requestLayout(); 388 return insets.consumeSystemWindowInsets(); 389 } 390 391 /** Notifies each task view of the user interaction. */ 392 public void onUserInteraction() { 393 // Get the first stack view 394 List<TaskStackView> stackViews = getTaskStackViews(); 395 int stackCount = stackViews.size(); 396 for (int i = 0; i < stackCount; i++) { 397 TaskStackView stackView = stackViews.get(i); 398 stackView.onUserInteraction(); 399 } 400 } 401 402 /** Focuses the next task in the first stack view */ 403 public void focusNextTask(boolean forward) { 404 // Get the first stack view 405 List<TaskStackView> stackViews = getTaskStackViews(); 406 if (!stackViews.isEmpty()) { 407 stackViews.get(0).focusNextTask(forward, true); 408 } 409 } 410 411 /** Dismisses the focused task. */ 412 public void dismissFocusedTask() { 413 // Get the first stack view 414 List<TaskStackView> stackViews = getTaskStackViews(); 415 if (!stackViews.isEmpty()) { 416 stackViews.get(0).dismissFocusedTask(); 417 } 418 } 419 420 /** Unfilters any filtered stacks */ 421 public boolean unfilterFilteredStacks() { 422 if (mStacks != null) { 423 // Check if there are any filtered stacks and unfilter them before we back out of Recents 424 boolean stacksUnfiltered = false; 425 int numStacks = mStacks.size(); 426 for (int i = 0; i < numStacks; i++) { 427 TaskStack stack = mStacks.get(i); 428 if (stack.hasFilteredTasks()) { 429 stack.unfilterTasks(); 430 stacksUnfiltered = true; 431 } 432 } 433 return stacksUnfiltered; 434 } 435 return false; 436 } 437 438 public void disableLayersForOneFrame() { 439 List<TaskStackView> stackViews = getTaskStackViews(); 440 for (int i = 0; i < stackViews.size(); i++) { 441 stackViews.get(i).disableLayersForOneFrame(); 442 } 443 } 444 445 private void postDrawHeaderThumbnailTransitionRunnable(final TaskView tv, final int offsetX, 446 final int offsetY, final TaskViewTransform transform, 447 final ActivityOptions.OnAnimationStartedListener animStartedListener) { 448 Runnable r = new Runnable() { 449 @Override 450 public void run() { 451 // Disable any focused state before we draw the header 452 if (tv.isFocusedTask()) { 453 tv.unsetFocusedTask(); 454 } 455 456 float scale = tv.getScaleX(); 457 int fromHeaderWidth = (int) (tv.mHeaderView.getMeasuredWidth() * scale); 458 int fromHeaderHeight = (int) (tv.mHeaderView.getMeasuredHeight() * scale); 459 460 Bitmap b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight, 461 Bitmap.Config.ARGB_8888); 462 if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) { 463 b.eraseColor(0xFFff0000); 464 } else { 465 Canvas c = new Canvas(b); 466 c.scale(tv.getScaleX(), tv.getScaleY()); 467 tv.mHeaderView.draw(c); 468 c.setBitmap(null); 469 } 470 b = b.createAshmemBitmap(); 471 int[] pts = new int[2]; 472 tv.getLocationOnScreen(pts); 473 try { 474 WindowManagerGlobal.getWindowManagerService() 475 .overridePendingAppTransitionAspectScaledThumb(b, 476 pts[0] + offsetX, 477 pts[1] + offsetY, 478 transform.rect.width(), 479 transform.rect.height(), 480 new IRemoteCallback.Stub() { 481 @Override 482 public void sendResult(Bundle data) 483 throws RemoteException { 484 post(new Runnable() { 485 @Override 486 public void run() { 487 if (animStartedListener != null) { 488 animStartedListener.onAnimationStarted(); 489 } 490 } 491 }); 492 } 493 }, true); 494 } catch (RemoteException e) { 495 Log.w(TAG, "Error overriding app transition", e); 496 } 497 } 498 }; 499 mCb.runAfterPause(r); 500 } 501 /**** TaskStackView.TaskStackCallbacks Implementation ****/ 502 503 @Override 504 public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv, 505 final TaskStack stack, final Task task, final boolean lockToTask) { 506 507 // Notify any callbacks of the launching of a new task 508 if (mCb != null) { 509 mCb.onTaskViewClicked(); 510 } 511 512 // Upfront the processing of the thumbnail 513 TaskViewTransform transform = new TaskViewTransform(); 514 View sourceView; 515 int offsetX = 0; 516 int offsetY = 0; 517 float stackScroll = stackView.getScroller().getStackScroll(); 518 if (tv == null) { 519 // If there is no actual task view, then use the stack view as the source view 520 // and then offset to the expected transform rect, but bound this to just 521 // outside the display rect (to ensure we don't animate from too far away) 522 sourceView = stackView; 523 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); 524 offsetX = transform.rect.left; 525 offsetY = mConfig.displayRect.height(); 526 } else { 527 sourceView = tv.mThumbnailView; 528 transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null); 529 } 530 531 // Compute the thumbnail to scale up from 532 final SystemServicesProxy ssp = 533 RecentsTaskLoader.getInstance().getSystemServicesProxy(); 534 ActivityOptions opts = null; 535 if (task.thumbnail != null && task.thumbnail.getWidth() > 0 && 536 task.thumbnail.getHeight() > 0) { 537 ActivityOptions.OnAnimationStartedListener animStartedListener = null; 538 if (lockToTask) { 539 animStartedListener = new ActivityOptions.OnAnimationStartedListener() { 540 boolean mTriggered = false; 541 @Override 542 public void onAnimationStarted() { 543 if (!mTriggered) { 544 postDelayed(new Runnable() { 545 @Override 546 public void run() { 547 mCb.onScreenPinningRequest(); 548 } 549 }, 350); 550 mTriggered = true; 551 } 552 } 553 }; 554 } 555 if (tv != null) { 556 postDrawHeaderThumbnailTransitionRunnable(tv, offsetX, offsetY, transform, 557 animStartedListener); 558 } 559 if (mConfig.multiStackEnabled) { 560 opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(), 561 R.anim.recents_from_unknown_enter, 562 R.anim.recents_from_unknown_exit, 563 sourceView.getHandler(), animStartedListener); 564 } else { 565 opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView, 566 Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8).createAshmemBitmap(), 567 offsetX, offsetY, transform.rect.width(), transform.rect.height(), 568 sourceView.getHandler(), animStartedListener); 569 } 570 } 571 572 final ActivityOptions launchOpts = opts; 573 final Runnable launchRunnable = new Runnable() { 574 @Override 575 public void run() { 576 if (task.isActive) { 577 // Bring an active task to the foreground 578 ssp.moveTaskToFront(task.key.id, launchOpts); 579 } else { 580 if (ssp.startActivityFromRecents(getContext(), task.key.id, 581 task.activityLabel, launchOpts)) { 582 if (launchOpts == null && lockToTask) { 583 mCb.onScreenPinningRequest(); 584 } 585 } else { 586 // Dismiss the task and return the user to home if we fail to 587 // launch the task 588 onTaskViewDismissed(task); 589 if (mCb != null) { 590 mCb.onTaskLaunchFailed(); 591 } 592 } 593 } 594 } 595 }; 596 597 // Launch the app right away if there is no task view, otherwise, animate the icon out first 598 if (tv == null) { 599 launchRunnable.run(); 600 } else { 601 if (task.group != null && !task.group.isFrontMostTask(task)) { 602 // For affiliated tasks that are behind other tasks, we must animate the front cards 603 // out of view before starting the task transition 604 stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask); 605 } else { 606 // Otherwise, we can start the task transition immediately 607 stackView.startLaunchTaskAnimation(tv, null, lockToTask); 608 launchRunnable.run(); 609 } 610 } 611 } 612 613 @Override 614 public void onTaskViewAppInfoClicked(Task t) { 615 // Create a new task stack with the application info details activity 616 Intent baseIntent = t.key.baseIntent; 617 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 618 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null)); 619 intent.setComponent(intent.resolveActivity(getContext().getPackageManager())); 620 TaskStackBuilder.create(getContext()) 621 .addNextIntentWithParentStack(intent).startActivities(null, 622 new UserHandle(t.key.userId)); 623 } 624 625 @Override 626 public void onTaskViewDismissed(Task t) { 627 // Remove any stored data from the loader. We currently don't bother notifying the views 628 // that the data has been unloaded because at the point we call onTaskViewDismissed(), the views 629 // either don't need to be updated, or have already been removed. 630 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 631 loader.deleteTaskData(t, false); 632 633 // Remove the old task from activity manager 634 loader.getSystemServicesProxy().removeTask(t.key.id); 635 } 636 637 @Override 638 public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks) { 639 if (removedTasks != null) { 640 int taskCount = removedTasks.size(); 641 for (int i = 0; i < taskCount; i++) { 642 onTaskViewDismissed(removedTasks.get(i)); 643 } 644 } 645 646 mCb.onAllTaskViewsDismissed(); 647 } 648 649 /** Final callback after Recents is finally hidden. */ 650 public void onRecentsHidden() { 651 // Notify each task stack view 652 List<TaskStackView> stackViews = getTaskStackViews(); 653 int stackCount = stackViews.size(); 654 for (int i = 0; i < stackCount; i++) { 655 TaskStackView stackView = stackViews.get(i); 656 stackView.onRecentsHidden(); 657 } 658 } 659 660 @Override 661 public void onTaskStackFilterTriggered() { 662 // Hide the search bar 663 if (mSearchBar != null) { 664 mSearchBar.animate() 665 .alpha(0f) 666 .setStartDelay(0) 667 .setInterpolator(mConfig.fastOutSlowInInterpolator) 668 .setDuration(mConfig.filteringCurrentViewsAnimDuration) 669 .withLayer() 670 .start(); 671 } 672 } 673 674 @Override 675 public void onTaskStackUnfilterTriggered() { 676 // Show the search bar 677 if (mSearchBar != null) { 678 mSearchBar.animate() 679 .alpha(1f) 680 .setStartDelay(0) 681 .setInterpolator(mConfig.fastOutSlowInInterpolator) 682 .setDuration(mConfig.filteringNewViewsAnimDuration) 683 .withLayer() 684 .start(); 685 } 686 } 687 688 @Override 689 public void onTaskResize(Task t) { 690 if (mCb != null) { 691 mCb.onTaskResize(t); 692 } 693 } 694 695 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ 696 697 @Override 698 public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) { 699 // Propagate this event down to each task stack view 700 List<TaskStackView> stackViews = getTaskStackViews(); 701 int stackCount = stackViews.size(); 702 for (int i = 0; i < stackCount; i++) { 703 TaskStackView stackView = stackViews.get(i); 704 stackView.onPackagesChanged(monitor, packageName, userId); 705 } 706 } 707} 708