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