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