RecentsView.java revision 036693cf71f8c4d2dd0dfb57ad05e8827ed6df60
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.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.content.Context; 22import android.graphics.Canvas; 23import android.graphics.Rect; 24import android.graphics.drawable.Drawable; 25import android.os.Handler; 26import android.util.ArraySet; 27import android.util.AttributeSet; 28import android.view.LayoutInflater; 29import android.view.MotionEvent; 30import android.view.View; 31import android.view.ViewPropertyAnimator; 32import android.view.WindowInsets; 33import android.view.animation.AnimationUtils; 34import android.view.animation.Interpolator; 35import android.widget.FrameLayout; 36import android.widget.TextView; 37import com.android.internal.logging.MetricsLogger; 38import com.android.systemui.R; 39import com.android.systemui.recents.Recents; 40import com.android.systemui.recents.RecentsActivity; 41import com.android.systemui.recents.RecentsActivityLaunchState; 42import com.android.systemui.recents.RecentsAppWidgetHostView; 43import com.android.systemui.recents.RecentsConfiguration; 44import com.android.systemui.recents.RecentsDebugFlags; 45import com.android.systemui.recents.events.EventBus; 46import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; 47import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; 48import com.android.systemui.recents.events.activity.HideHistoryButtonEvent; 49import com.android.systemui.recents.events.activity.HideHistoryEvent; 50import com.android.systemui.recents.events.activity.LaunchTaskEvent; 51import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent; 52import com.android.systemui.recents.events.activity.ShowHistoryEvent; 53import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent; 54import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; 55import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; 56import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; 57import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; 58import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 59import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; 60import com.android.systemui.recents.misc.ReferenceCountedTrigger; 61import com.android.systemui.recents.misc.SystemServicesProxy; 62import com.android.systemui.recents.model.Task; 63import com.android.systemui.recents.model.TaskStack; 64import com.android.systemui.stackdivider.WindowManagerProxy; 65import com.android.systemui.statusbar.FlingAnimationUtils; 66import com.android.systemui.statusbar.phone.PhoneStatusBar; 67 68import java.util.ArrayList; 69import java.util.Collections; 70import java.util.List; 71 72import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 73 74/** 75 * This view is the the top level layout that contains TaskStacks (which are laid out according 76 * to their SpaceNode bounds. 77 */ 78public class RecentsView extends FrameLayout { 79 80 private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135; 81 82 private final Handler mHandler; 83 84 private TaskStack mStack; 85 private TaskStackView mTaskStackView; 86 private RecentsAppWidgetHostView mSearchBar; 87 private TextView mHistoryButton; 88 private View mEmptyView; 89 private boolean mAwaitingFirstLayout = true; 90 private boolean mLastTaskLaunchedWasFreeform; 91 private Rect mSystemInsets = new Rect(); 92 private int mDividerSize; 93 94 private RecentsTransitionHelper mTransitionHelper; 95 private RecentsViewTouchHandler mTouchHandler; 96 97 private final Interpolator mFastOutSlowInInterpolator; 98 private final Interpolator mFastOutLinearInInterpolator; 99 private final FlingAnimationUtils mFlingAnimationUtils; 100 101 public RecentsView(Context context) { 102 this(context, null); 103 } 104 105 public RecentsView(Context context, AttributeSet attrs) { 106 this(context, attrs, 0); 107 } 108 109 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { 110 this(context, attrs, defStyleAttr, 0); 111 } 112 113 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 114 super(context, attrs, defStyleAttr, defStyleRes); 115 setWillNotDraw(false); 116 117 SystemServicesProxy ssp = Recents.getSystemServices(); 118 mHandler = new Handler(); 119 mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler); 120 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 121 com.android.internal.R.interpolator.fast_out_slow_in); 122 mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, 123 com.android.internal.R.interpolator.fast_out_linear_in); 124 mDividerSize = ssp.getDockedDividerSize(context); 125 mTouchHandler = new RecentsViewTouchHandler(this); 126 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f); 127 128 LayoutInflater inflater = LayoutInflater.from(context); 129 mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this, false); 130 mHistoryButton.setOnClickListener(new View.OnClickListener() { 131 @Override 132 public void onClick(View v) { 133 EventBus.getDefault().send(new ShowHistoryEvent()); 134 } 135 }); 136 addView(mHistoryButton); 137 mEmptyView = inflater.inflate(R.layout.recents_empty, this, false); 138 addView(mEmptyView); 139 } 140 141 /** Set/get the bsp root node */ 142 public void setTaskStack(TaskStack stack) { 143 RecentsConfiguration config = Recents.getConfiguration(); 144 RecentsActivityLaunchState launchState = config.getLaunchState(); 145 mStack = stack; 146 if (launchState.launchedReuseTaskStackViews) { 147 if (mTaskStackView != null) { 148 // If onRecentsHidden is not triggered, we need to the stack view again here 149 mTaskStackView.reset(); 150 mTaskStackView.setStack(stack); 151 } else { 152 mTaskStackView = new TaskStackView(getContext(), stack); 153 addView(mTaskStackView); 154 } 155 } else { 156 if (mTaskStackView != null) { 157 removeView(mTaskStackView); 158 } 159 mTaskStackView = new TaskStackView(getContext(), stack); 160 addView(mTaskStackView); 161 } 162 163 // Update the top level view's visibilities 164 if (stack.getTaskCount() > 0) { 165 hideEmptyView(); 166 } else { 167 showEmptyView(); 168 } 169 170 // Trigger a new layout 171 requestLayout(); 172 } 173 174 /** 175 * Returns whether the last task launched was in the freeform stack or not. 176 */ 177 public boolean isLastTaskLaunchedFreeform() { 178 return mLastTaskLaunchedWasFreeform; 179 } 180 181 /** 182 * Returns the currently set task stack. 183 */ 184 public TaskStack getTaskStack() { 185 return mStack; 186 } 187 188 /** Gets the next task in the stack - or if the last - the top task */ 189 public Task getNextTaskOrTopTask(Task taskToSearch) { 190 Task returnTask = null; 191 boolean found = false; 192 if (mTaskStackView != null) { 193 TaskStack stack = mTaskStackView.getStack(); 194 ArrayList<Task> taskList = stack.getStackTasks(); 195 // Iterate the stack views and try and find the focused task 196 for (int j = taskList.size() - 1; j >= 0; --j) { 197 Task task = taskList.get(j); 198 // Return the next task in the line. 199 if (found) 200 return task; 201 // Remember the first possible task as the top task. 202 if (returnTask == null) 203 returnTask = task; 204 if (task == taskToSearch) 205 found = true; 206 } 207 } 208 return returnTask; 209 } 210 211 /** Launches the focused task from the first stack if possible */ 212 public boolean launchFocusedTask() { 213 if (mTaskStackView != null) { 214 Task task = mTaskStackView.getFocusedTask(); 215 if (task != null) { 216 TaskView taskView = mTaskStackView.getChildViewForTask(task); 217 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 218 INVALID_STACK_ID, false)); 219 return true; 220 } 221 } 222 return false; 223 } 224 225 /** Launches the task that recents was launched from if possible */ 226 public boolean launchPreviousTask() { 227 if (mTaskStackView != null) { 228 TaskStack stack = mTaskStackView.getStack(); 229 Task task = stack.getLaunchTarget(); 230 if (task != null) { 231 TaskView taskView = mTaskStackView.getChildViewForTask(task); 232 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 233 INVALID_STACK_ID, false)); 234 return true; 235 } 236 } 237 return false; 238 } 239 240 /** Launches a given task. */ 241 public boolean launchTask(Task task, Rect taskBounds, int destinationStack) { 242 if (mTaskStackView != null) { 243 // Iterate the stack views and try and find the given task. 244 List<TaskView> taskViews = mTaskStackView.getTaskViews(); 245 int taskViewCount = taskViews.size(); 246 for (int j = 0; j < taskViewCount; j++) { 247 TaskView tv = taskViews.get(j); 248 if (tv.getTask() == task) { 249 EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds, 250 destinationStack, false)); 251 return true; 252 } 253 } 254 } 255 return false; 256 } 257 258 /** Adds the search bar */ 259 public void setSearchBar(RecentsAppWidgetHostView searchBar) { 260 // Remove the previous search bar if one exists 261 if (mSearchBar != null && indexOfChild(mSearchBar) > -1) { 262 removeView(mSearchBar); 263 } 264 // Add the new search bar 265 if (searchBar != null) { 266 mSearchBar = searchBar; 267 addView(mSearchBar); 268 } 269 } 270 271 /** Returns whether there is currently a search bar */ 272 public boolean hasValidSearchBar() { 273 return mSearchBar != null && !mSearchBar.isReinflateRequired(); 274 } 275 276 /** 277 * Hides the task stack and shows the empty view. 278 */ 279 public void showEmptyView() { 280 if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) { 281 mSearchBar.setVisibility(View.INVISIBLE); 282 } 283 mTaskStackView.setVisibility(View.INVISIBLE); 284 mEmptyView.setVisibility(View.VISIBLE); 285 mEmptyView.bringToFront(); 286 mHistoryButton.bringToFront(); 287 } 288 289 /** 290 * Shows the task stack and hides the empty view. 291 */ 292 public void hideEmptyView() { 293 mEmptyView.setVisibility(View.INVISIBLE); 294 mTaskStackView.setVisibility(View.VISIBLE); 295 if (RecentsDebugFlags.Static.EnableSearchBar && (mSearchBar != null)) { 296 mSearchBar.setVisibility(View.VISIBLE); 297 } 298 mTaskStackView.bringToFront(); 299 if (mSearchBar != null) { 300 mSearchBar.bringToFront(); 301 } 302 mHistoryButton.bringToFront(); 303 } 304 305 /** 306 * Returns the last known system insets. 307 */ 308 public Rect getSystemInsets() { 309 return mSystemInsets; 310 } 311 312 @Override 313 protected void onAttachedToWindow() { 314 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); 315 EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1); 316 super.onAttachedToWindow(); 317 } 318 319 @Override 320 protected void onDetachedFromWindow() { 321 super.onDetachedFromWindow(); 322 EventBus.getDefault().unregister(this); 323 EventBus.getDefault().unregister(mTouchHandler); 324 } 325 326 /** 327 * This is called with the full size of the window since we are handling our own insets. 328 */ 329 @Override 330 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 331 RecentsConfiguration config = Recents.getConfiguration(); 332 int width = MeasureSpec.getSize(widthMeasureSpec); 333 int height = MeasureSpec.getSize(heightMeasureSpec); 334 335 // Get the search bar bounds and measure the search bar layout 336 Rect searchBarSpaceBounds = new Rect(); 337 if (mSearchBar != null) { 338 config.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top, 339 searchBarSpaceBounds); 340 mSearchBar.measure( 341 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY), 342 MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY)); 343 } 344 345 Rect taskStackBounds = new Rect(); 346 config.getTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top, 347 mSystemInsets.right, searchBarSpaceBounds, taskStackBounds); 348 if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) { 349 mTaskStackView.setTaskStackBounds(taskStackBounds, mSystemInsets); 350 mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec); 351 } 352 353 // Measure the empty view 354 measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 355 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); 356 357 // Measure the history button with the full space above the stack, but width-constrained 358 // to the stack 359 Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect; 360 measureChild(mHistoryButton, 361 MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.EXACTLY), 362 MeasureSpec.makeMeasureSpec(historyButtonRect.height(), 363 MeasureSpec.EXACTLY)); 364 365 setMeasuredDimension(width, height); 366 } 367 368 /** 369 * This is called with the full size of the window since we are handling our own insets. 370 */ 371 @Override 372 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 373 RecentsConfiguration config = Recents.getConfiguration(); 374 375 // Get the search bar bounds so that we lay it out 376 Rect measuredRect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight()); 377 Rect searchBarSpaceBounds = new Rect(); 378 if (mSearchBar != null) { 379 config.getSearchBarBounds(measuredRect, 380 mSystemInsets.top, searchBarSpaceBounds); 381 mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top, 382 searchBarSpaceBounds.right, searchBarSpaceBounds.bottom); 383 } 384 385 if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) { 386 mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); 387 } 388 389 // Layout the empty view 390 mEmptyView.layout(left, top, right, bottom); 391 392 // Layout the history button left-aligned with the stack, but offset from the top of the 393 // view 394 Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect; 395 mHistoryButton.layout(historyButtonRect.left, historyButtonRect.top, 396 historyButtonRect.right, historyButtonRect.bottom); 397 398 if (mAwaitingFirstLayout) { 399 mAwaitingFirstLayout = false; 400 401 // If launched via dragging from the nav bar, then we should translate the whole view 402 // down offscreen 403 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 404 if (launchState.launchedViaDragGesture) { 405 setTranslationY(getMeasuredHeight()); 406 } else { 407 setTranslationY(0f); 408 } 409 } 410 } 411 412 @Override 413 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 414 mSystemInsets.set(insets.getSystemWindowInsets()); 415 requestLayout(); 416 return insets; 417 } 418 419 @Override 420 public boolean onInterceptTouchEvent(MotionEvent ev) { 421 return mTouchHandler.onInterceptTouchEvent(ev); 422 } 423 424 @Override 425 public boolean onTouchEvent(MotionEvent ev) { 426 return mTouchHandler.onTouchEvent(ev); 427 } 428 429 @Override 430 protected void dispatchDraw(Canvas canvas) { 431 super.dispatchDraw(canvas); 432 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 433 for (int i = visDockStates.size() - 1; i >= 0; i--) { 434 Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; 435 if (d.getAlpha() > 0) { 436 d.draw(canvas); 437 } 438 } 439 } 440 441 @Override 442 protected boolean verifyDrawable(Drawable who) { 443 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 444 for (int i = visDockStates.size() - 1; i >= 0; i--) { 445 Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; 446 if (d == who) { 447 return true; 448 } 449 } 450 return super.verifyDrawable(who); 451 } 452 453 /**** EventBus Events ****/ 454 455 public final void onBusEvent(LaunchTaskEvent event) { 456 mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); 457 mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView, 458 event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack); 459 } 460 461 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { 462 // Hide the history button 463 int taskViewExitToHomeDuration = getResources().getInteger( 464 R.integer.recents_task_exit_to_home_duration); 465 hideHistoryButton(taskViewExitToHomeDuration); 466 467 // If we are going home, cancel the previous task's window transition 468 EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null)); 469 } 470 471 public final void onBusEvent(DragStartEvent event) { 472 updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), 473 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 474 true /* animateAlpha */, false /* animateBounds */); 475 } 476 477 public final void onBusEvent(DragDropTargetChangedEvent event) { 478 if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { 479 updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), 480 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 481 true /* animateAlpha */, true /* animateBounds */); 482 } else { 483 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 484 updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, 485 false /* isDefaultDockState */, -1, true /* animateAlpha */, 486 true /* animateBounds */); 487 } 488 } 489 490 public final void onBusEvent(final DragEndEvent event) { 491 // Handle the case where we drop onto a dock region 492 if (event.dropTarget instanceof TaskStack.DockState) { 493 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 494 495 // Hide the dock region 496 updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, 497 false /* animateAlpha */, false /* animateBounds */); 498 499 TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm(); 500 TaskStackViewScroller stackScroller = mTaskStackView.getScroller(); 501 TaskViewTransform tmpTransform = new TaskViewTransform(); 502 503 // We translated the view but we need to animate it back from the current layout-space 504 // rect to its final layout-space rect 505 int x = (int) event.taskView.getTranslationX(); 506 int y = (int) event.taskView.getTranslationY(); 507 Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(), 508 event.taskView.getRight(), event.taskView.getBottom()); 509 taskViewRect.offset(x, y); 510 event.taskView.setTranslationX(0); 511 event.taskView.setTranslationY(0); 512 event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top, 513 taskViewRect.right, taskViewRect.bottom); 514 515 // Remove the task view after it is docked 516 mTaskStackView.updateLayout(false /* boundScroll */); 517 stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform, 518 null); 519 tmpTransform.alpha = 0; 520 tmpTransform.scale = 1f; 521 tmpTransform.rect.set(taskViewRect); 522 mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform, 523 new TaskViewAnimation(125, PhoneStatusBar.ALPHA_OUT, 524 new AnimatorListenerAdapter() { 525 @Override 526 public void onAnimationEnd(Animator animation) { 527 // Dock the task and launch it 528 SystemServicesProxy ssp = Recents.getSystemServices(); 529 ssp.startTaskInDockedMode(getContext(), event.task.key.id, 530 dockState.createMode); 531 launchTask(event.task, null, INVALID_STACK_ID); 532 533 mTaskStackView.getStack().removeTask(event.task); 534 } 535 })); 536 537 538 MetricsLogger.action(mContext, 539 MetricsLogger.ACTION_WINDOW_DOCK_DRAG_DROP); 540 } else { 541 // Animate the overlay alpha back to 0 542 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, 543 true /* animateAlpha */, false /* animateBounds */); 544 } 545 } 546 547 public final void onBusEvent(DraggingInRecentsEvent event) { 548 if (mTaskStackView.getTaskViews().size() > 0) { 549 setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY()); 550 } 551 } 552 553 public final void onBusEvent(DraggingInRecentsEndedEvent event) { 554 ViewPropertyAnimator animator = animate(); 555 if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 556 animator.translationY(getHeight()); 557 animator.withEndAction(new Runnable() { 558 @Override 559 public void run() { 560 WindowManagerProxy.getInstance().maximizeDockedStack(); 561 } 562 }); 563 mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity); 564 } else { 565 animator.translationY(0f); 566 animator.setListener(null); 567 mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity); 568 } 569 animator.start(); 570 } 571 572 public final void onBusEvent(ShowHistoryEvent event) { 573 // Hide the history button when the history view is shown 574 hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration), 575 event.getAnimationTrigger()); 576 event.addPostAnimationCallback(new Runnable() { 577 @Override 578 public void run() { 579 setAlpha(0f); 580 } 581 }); 582 } 583 584 public final void onBusEvent(HideHistoryEvent event) { 585 // Show the history button when the history view is hidden 586 setAlpha(1f); 587 showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration), 588 event.getAnimationTrigger()); 589 } 590 591 public final void onBusEvent(ShowHistoryButtonEvent event) { 592 showHistoryButton(150); 593 } 594 595 public final void onBusEvent(HideHistoryButtonEvent event) { 596 hideHistoryButton(100); 597 } 598 599 public final void onBusEvent(TaskStackUpdatedEvent event) { 600 mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */); 601 mStack.createAffiliatedGroupings(getContext()); 602 } 603 604 /** 605 * Shows the history button. 606 */ 607 private void showHistoryButton(final int duration) { 608 ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 609 showHistoryButton(duration, postAnimationTrigger); 610 postAnimationTrigger.flushLastDecrementRunnables(); 611 } 612 613 private void showHistoryButton(final int duration, 614 final ReferenceCountedTrigger postHideHistoryAnimationTrigger) { 615 mHistoryButton.setText(getContext().getString(R.string.recents_history_label_format, 616 mStack.getHistoricalTasks().size())); 617 if (mHistoryButton.getVisibility() == View.INVISIBLE) { 618 mHistoryButton.setVisibility(View.VISIBLE); 619 mHistoryButton.setAlpha(0f); 620 postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() { 621 @Override 622 public void run() { 623 mHistoryButton.animate() 624 .alpha(1f) 625 .setDuration(duration) 626 .setInterpolator(mFastOutSlowInInterpolator) 627 .withLayer() 628 .start(); 629 } 630 }); 631 } 632 } 633 634 /** 635 * Hides the history button. 636 */ 637 private void hideHistoryButton(int duration) { 638 ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 639 hideHistoryButton(duration, postAnimationTrigger); 640 postAnimationTrigger.flushLastDecrementRunnables(); 641 } 642 643 private void hideHistoryButton(int duration, 644 final ReferenceCountedTrigger postHideStackAnimationTrigger) { 645 if (mHistoryButton.getVisibility() == View.VISIBLE) { 646 mHistoryButton.animate() 647 .alpha(0f) 648 .setDuration(duration) 649 .setInterpolator(mFastOutLinearInInterpolator) 650 .withEndAction(new Runnable() { 651 @Override 652 public void run() { 653 mHistoryButton.setVisibility(View.INVISIBLE); 654 postHideStackAnimationTrigger.decrement(); 655 } 656 }) 657 .withLayer() 658 .start(); 659 postHideStackAnimationTrigger.increment(); 660 } 661 } 662 663 /** 664 * Updates the dock region to match the specified dock state. 665 */ 666 private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, 667 boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha, 668 boolean animateBounds) { 669 ArraySet<TaskStack.DockState> newDockStatesSet = new ArraySet<>(); 670 if (newDockStates != null) { 671 Collections.addAll(newDockStatesSet, newDockStates); 672 } 673 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 674 for (int i = visDockStates.size() - 1; i >= 0; i--) { 675 TaskStack.DockState dockState = visDockStates.get(i); 676 TaskStack.DockState.ViewState viewState = dockState.viewState; 677 if (newDockStates == null || !newDockStatesSet.contains(dockState)) { 678 // This is no longer visible, so hide it 679 viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION, 680 PhoneStatusBar.ALPHA_OUT, animateAlpha, animateBounds); 681 } else { 682 // This state is now visible, update the bounds and show it 683 int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha); 684 Rect bounds = isDefaultDockState 685 ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight()) 686 : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(), 687 mDividerSize, mSystemInsets, getResources()); 688 if (viewState.dockAreaOverlay.getCallback() != this) { 689 viewState.dockAreaOverlay.setCallback(this); 690 viewState.dockAreaOverlay.setBounds(bounds); 691 } 692 viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION, 693 PhoneStatusBar.ALPHA_IN, animateAlpha, animateBounds); 694 } 695 } 696 } 697 698 public final void onBusEvent(RecentsVisibilityChangedEvent event) { 699 if (!event.visible) { 700 // Reset the view state 701 mAwaitingFirstLayout = true; 702 mLastTaskLaunchedWasFreeform = false; 703 } 704 } 705} 706