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 { onTaskViewClicked()65 public void onTaskViewClicked(); onTaskLaunchFailed()66 public void onTaskLaunchFailed(); onAllTaskViewsDismissed()67 public void onAllTaskViewsDismissed(); onExitToHomeAnimationTriggered()68 public void onExitToHomeAnimationTriggered(); onScreenPinningRequest()69 public void onScreenPinningRequest(); onTaskResize(Task t)70 public void onTaskResize(Task t); runAfterPause(Runnable r)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 RecentsView(Context context)84 public RecentsView(Context context) { 85 super(context); 86 } 87 RecentsView(Context context, AttributeSet attrs)88 public RecentsView(Context context, AttributeSet attrs) { 89 this(context, attrs, 0); 90 } 91 RecentsView(Context context, AttributeSet attrs, int defStyleAttr)92 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { 93 this(context, attrs, defStyleAttr, 0); 94 } 95 RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 */ setCallbacks(RecentsViewCallbacks cb)104 public void setCallbacks(RecentsViewCallbacks cb) { 105 mCb = cb; 106 } 107 108 /** Sets the debug overlay */ setDebugOverlay(DebugOverlayView overlay)109 public void setDebugOverlay(DebugOverlayView overlay) { 110 mDebugOverlay = overlay; 111 } 112 113 /** Set/get the bsp root node */ setTaskStacks(ArrayList<TaskStack> stacks)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 */ getTaskStackViews()157 List<TaskStackView> getTaskStackViews() { 158 return mTaskStackViews; 159 } 160 161 /** Gets the next task in the stack - or if the last - the top task */ getNextTaskOrTopTask(Task taskToSearch)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 */ launchFocusedTask()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. */ launchTask(Task 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 */ launchPreviousTask()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 */ startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx)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 */ startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx)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 */ setSearchBar(RecentsAppWidgetHostView searchBar)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 */ hasValidSearchBar()303 public boolean hasValidSearchBar() { 304 return mSearchBar != null && !mSearchBar.isReinflateRequired(); 305 } 306 307 /** Sets the visibility of the search bar */ setSearchBarVisibility(int visibility)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 onMeasure(int widthMeasureSpec, int heightMeasureSpec)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 onLayout(boolean changed, int left, int top, int right, int bottom)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 onApplyWindowInsets(WindowInsets insets)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. */ onUserInteraction()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 */ focusNextTask(boolean forward)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. */ dismissFocusedTask()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 */ unfilterFilteredStacks()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 disableLayersForOneFrame()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 postDrawHeaderThumbnailTransitionRunnable(final TaskView tv, final int offsetX, final int offsetY, final TaskViewTransform transform, final ActivityOptions.OnAnimationStartedListener animStartedListener)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 onTaskViewClicked(final TaskStackView stackView, final TaskView tv, final TaskStack stack, final Task task, final boolean lockToTask)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 onTaskViewAppInfoClicked(Task t)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 onTaskViewDismissed(Task t)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 onAllTaskViewsDismissed(ArrayList<Task> removedTasks)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. */ onRecentsHidden()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 onTaskStackFilterTriggered()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 onTaskStackUnfilterTriggered()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 onTaskResize(Task t)704 public void onTaskResize(Task t) { 705 if (mCb != null) { 706 mCb.onTaskResize(t); 707 } 708 } 709 710 /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ 711 712 @Override onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId)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