RecentsView.java revision 6672fb4e2202f5f5718409be98ffd7538dc4dfa1
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 static android.app.ActivityManager.StackId.INVALID_STACK_ID; 20 21import android.animation.Animator; 22import android.animation.ObjectAnimator; 23import android.app.ActivityOptions.OnAnimationStartedListener; 24import android.app.WallpaperManager; 25import android.content.Context; 26import android.graphics.Canvas; 27import android.graphics.Color; 28import android.graphics.Outline; 29import android.graphics.Rect; 30import android.graphics.drawable.ColorDrawable; 31import android.graphics.drawable.Drawable; 32import android.util.ArraySet; 33import android.util.AttributeSet; 34import android.view.AppTransitionAnimationSpec; 35import android.view.IAppTransitionAnimationSpecsFuture; 36import android.view.LayoutInflater; 37import android.view.MotionEvent; 38import android.view.View; 39import android.view.ViewDebug; 40import android.view.ViewOutlineProvider; 41import android.view.ViewPropertyAnimator; 42import android.view.WindowInsets; 43import android.widget.FrameLayout; 44import android.widget.TextView; 45 46import com.android.internal.logging.MetricsLogger; 47import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 48import com.android.systemui.Interpolators; 49import com.android.systemui.R; 50import com.android.systemui.recents.Recents; 51import com.android.systemui.recents.RecentsActivity; 52import com.android.systemui.recents.RecentsActivityLaunchState; 53import com.android.systemui.recents.RecentsConfiguration; 54import com.android.systemui.recents.RecentsDebugFlags; 55import com.android.systemui.recents.events.EventBus; 56import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; 57import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; 58import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; 59import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; 60import com.android.systemui.recents.events.activity.LaunchTaskEvent; 61import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; 62import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; 63import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; 64import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; 65import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; 66import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; 67import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; 68import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; 69import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 70import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; 71import com.android.systemui.recents.misc.ReferenceCountedTrigger; 72import com.android.systemui.recents.misc.SystemServicesProxy; 73import com.android.systemui.recents.misc.Utilities; 74import com.android.systemui.recents.model.Task; 75import com.android.systemui.recents.model.TaskStack; 76import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer; 77import com.android.systemui.stackdivider.WindowManagerProxy; 78import com.android.systemui.statusbar.FlingAnimationUtils; 79 80import com.google.android.colorextraction.ColorExtractor; 81import com.google.android.colorextraction.drawable.GradientDrawable; 82 83import java.io.FileDescriptor; 84import java.io.PrintWriter; 85import java.util.ArrayList; 86import java.util.List; 87 88/** 89 * This view is the the top level layout that contains TaskStacks (which are laid out according 90 * to their SpaceNode bounds. 91 */ 92public class RecentsView extends FrameLayout implements ColorExtractor.OnColorsChangedListener { 93 94 private static final String TAG = "RecentsView"; 95 96 private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200; 97 private static final float DEFAULT_SCRIM_ALPHA = 0.8f; 98 99 private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134; 100 private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100; 101 102 private TaskStackView mTaskStackView; 103 private TextView mStackActionButton; 104 private TextView mEmptyView; 105 106 private boolean mAwaitingFirstLayout = true; 107 private boolean mLastTaskLaunchedWasFreeform; 108 109 @ViewDebug.ExportedProperty(category="recents") 110 Rect mSystemInsets = new Rect(); 111 private int mDividerSize; 112 113 private final float mScrimAlpha; 114 private final GradientDrawable mBackgroundScrim; 115 private final ColorExtractor mColorExtractor; 116 private Animator mBackgroundScrimAnimator; 117 118 private RecentsTransitionHelper mTransitionHelper; 119 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_") 120 private RecentsViewTouchHandler mTouchHandler; 121 private final FlingAnimationUtils mFlingAnimationUtils; 122 123 public RecentsView(Context context) { 124 this(context, null); 125 } 126 127 public RecentsView(Context context, AttributeSet attrs) { 128 this(context, attrs, 0); 129 } 130 131 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { 132 this(context, attrs, defStyleAttr, 0); 133 } 134 135 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 136 super(context, attrs, defStyleAttr, defStyleRes); 137 setWillNotDraw(false); 138 139 SystemServicesProxy ssp = Recents.getSystemServices(); 140 mTransitionHelper = new RecentsTransitionHelper(getContext()); 141 mDividerSize = ssp.getDockedDividerSize(context); 142 mTouchHandler = new RecentsViewTouchHandler(this); 143 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f); 144 mScrimAlpha = DEFAULT_SCRIM_ALPHA; 145 mBackgroundScrim = new GradientDrawable(context); 146 mBackgroundScrim.setCallback(this); 147 mBackgroundScrim.setAlpha((int) (mScrimAlpha * 255)); 148 mColorExtractor = new ColorExtractor(context); 149 mColorExtractor.setListener(this); 150 // We don't want to interpolate colors because we're defining the initial state. 151 // Gradient should be set/ready when you open "Recents". 152 mBackgroundScrim.setColors(mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM), false); 153 154 LayoutInflater inflater = LayoutInflater.from(context); 155 if (RecentsDebugFlags.Static.EnableStackActionButton) { 156 mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button, 157 this, false); 158 mStackActionButton.setOnClickListener(new View.OnClickListener() { 159 @Override 160 public void onClick(View v) { 161 EventBus.getDefault().send(new DismissAllTaskViewsEvent()); 162 } 163 }); 164 addView(mStackActionButton); 165 } 166 mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false); 167 addView(mEmptyView); 168 } 169 170 /** 171 * Called from RecentsActivity when it is relaunched. 172 */ 173 public void onReload(boolean isResumingFromVisible, boolean isTaskStackEmpty) { 174 RecentsConfiguration config = Recents.getConfiguration(); 175 RecentsActivityLaunchState launchState = config.getLaunchState(); 176 177 if (mTaskStackView == null) { 178 isResumingFromVisible = false; 179 mTaskStackView = new TaskStackView(getContext()); 180 mTaskStackView.setSystemInsets(mSystemInsets); 181 addView(mTaskStackView); 182 } 183 184 // Reset the state 185 mAwaitingFirstLayout = !isResumingFromVisible; 186 mLastTaskLaunchedWasFreeform = false; 187 188 // Update the stack 189 mTaskStackView.onReload(isResumingFromVisible); 190 191 if (isResumingFromVisible) { 192 // If we are already visible, then restore the background scrim 193 animateBackgroundScrim(1f, DEFAULT_UPDATE_SCRIM_DURATION); 194 } else { 195 // If we are already occluded by the app, then set the final background scrim alpha now. 196 // Otherwise, defer until the enter animation completes to animate the scrim alpha with 197 // the tasks for the home animation. 198 if (launchState.launchedViaDockGesture || launchState.launchedFromApp 199 || isTaskStackEmpty) { 200 mBackgroundScrim.setAlpha((int) (mScrimAlpha * 255)); 201 } else { 202 mBackgroundScrim.setAlpha(0); 203 } 204 } 205 } 206 207 /** 208 * Called from RecentsActivity when the task stack is updated. 209 */ 210 public void updateStack(TaskStack stack, boolean setStackViewTasks) { 211 if (setStackViewTasks) { 212 mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */); 213 } 214 215 // Update the top level view's visibilities 216 if (stack.getTaskCount() > 0) { 217 hideEmptyView(); 218 } else { 219 showEmptyView(R.string.recents_empty_message); 220 } 221 } 222 223 /** 224 * Returns the current TaskStack. 225 */ 226 public TaskStack getStack() { 227 return mTaskStackView.getStack(); 228 } 229 230 /* 231 * Returns the window background scrim. 232 */ 233 public Drawable getBackgroundScrim() { 234 return mBackgroundScrim; 235 } 236 237 /** 238 * Returns whether the last task launched was in the freeform stack or not. 239 */ 240 public boolean isLastTaskLaunchedFreeform() { 241 return mLastTaskLaunchedWasFreeform; 242 } 243 244 /** Launches the focused task from the first stack if possible */ 245 public boolean launchFocusedTask(int logEvent) { 246 if (mTaskStackView != null) { 247 Task task = mTaskStackView.getFocusedTask(); 248 if (task != null) { 249 TaskView taskView = mTaskStackView.getChildViewForTask(task); 250 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 251 INVALID_STACK_ID, false)); 252 253 if (logEvent != 0) { 254 MetricsLogger.action(getContext(), logEvent, 255 task.key.getComponent().toString()); 256 } 257 return true; 258 } 259 } 260 return false; 261 } 262 263 /** Launches the task that recents was launched from if possible */ 264 public boolean launchPreviousTask() { 265 if (mTaskStackView != null) { 266 Task task = getStack().getLaunchTarget(); 267 if (task != null) { 268 TaskView taskView = mTaskStackView.getChildViewForTask(task); 269 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 270 INVALID_STACK_ID, false)); 271 return true; 272 } 273 } 274 return false; 275 } 276 277 /** Launches a given task. */ 278 public boolean launchTask(Task task, Rect taskBounds, int destinationStack) { 279 if (mTaskStackView != null) { 280 // Iterate the stack views and try and find the given task. 281 List<TaskView> taskViews = mTaskStackView.getTaskViews(); 282 int taskViewCount = taskViews.size(); 283 for (int j = 0; j < taskViewCount; j++) { 284 TaskView tv = taskViews.get(j); 285 if (tv.getTask() == task) { 286 EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds, 287 destinationStack, false)); 288 return true; 289 } 290 } 291 } 292 return false; 293 } 294 295 /** 296 * Hides the task stack and shows the empty view. 297 */ 298 public void showEmptyView(int msgResId) { 299 mTaskStackView.setVisibility(View.INVISIBLE); 300 mEmptyView.setText(msgResId); 301 mEmptyView.setVisibility(View.VISIBLE); 302 mEmptyView.bringToFront(); 303 if (RecentsDebugFlags.Static.EnableStackActionButton) { 304 mStackActionButton.bringToFront(); 305 } 306 } 307 308 /** 309 * Shows the task stack and hides the empty view. 310 */ 311 public void hideEmptyView() { 312 mEmptyView.setVisibility(View.INVISIBLE); 313 mTaskStackView.setVisibility(View.VISIBLE); 314 mTaskStackView.bringToFront(); 315 if (RecentsDebugFlags.Static.EnableStackActionButton) { 316 mStackActionButton.bringToFront(); 317 } 318 } 319 320 @Override 321 protected void onAttachedToWindow() { 322 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); 323 EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2); 324 super.onAttachedToWindow(); 325 } 326 327 @Override 328 protected void onDetachedFromWindow() { 329 super.onDetachedFromWindow(); 330 EventBus.getDefault().unregister(this); 331 EventBus.getDefault().unregister(mTouchHandler); 332 } 333 334 /** 335 * This is called with the full size of the window since we are handling our own insets. 336 */ 337 @Override 338 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 339 int width = MeasureSpec.getSize(widthMeasureSpec); 340 int height = MeasureSpec.getSize(heightMeasureSpec); 341 342 if (mTaskStackView.getVisibility() != GONE) { 343 mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec); 344 } 345 346 // Measure the empty view to the full size of the screen 347 if (mEmptyView.getVisibility() != GONE) { 348 measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), 349 MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); 350 } 351 352 if (RecentsDebugFlags.Static.EnableStackActionButton) { 353 // Measure the stack action button within the constraints of the space above the stack 354 Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect(); 355 measureChild(mStackActionButton, 356 MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST), 357 MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST)); 358 } 359 360 setMeasuredDimension(width, height); 361 } 362 363 /** 364 * This is called with the full size of the window since we are handling our own insets. 365 */ 366 @Override 367 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 368 if (mTaskStackView.getVisibility() != GONE) { 369 mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); 370 } 371 372 // Layout the empty view 373 if (mEmptyView.getVisibility() != GONE) { 374 int leftRightInsets = mSystemInsets.left + mSystemInsets.right; 375 int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom; 376 int childWidth = mEmptyView.getMeasuredWidth(); 377 int childHeight = mEmptyView.getMeasuredHeight(); 378 int childLeft = left + mSystemInsets.left + 379 Math.max(0, (right - left - leftRightInsets - childWidth)) / 2; 380 int childTop = top + mSystemInsets.top + 381 Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2; 382 mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); 383 } 384 385 // Needs to know the screen size since the gradient never scales up or down 386 // even when bounds change. 387 mBackgroundScrim.setScreenSize(right - left, bottom - top); 388 mBackgroundScrim.setBounds(left, top, right, bottom); 389 390 if (RecentsDebugFlags.Static.EnableStackActionButton) { 391 // Layout the stack action button such that its drawable is start-aligned with the 392 // stack, vertically centered in the available space above the stack 393 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); 394 mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right, 395 buttonBounds.bottom); 396 } 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 mTaskStackView.setSystemInsets(mSystemInsets); 416 requestLayout(); 417 return insets; 418 } 419 420 @Override 421 public boolean onInterceptTouchEvent(MotionEvent ev) { 422 return mTouchHandler.onInterceptTouchEvent(ev); 423 } 424 425 @Override 426 public boolean onTouchEvent(MotionEvent ev) { 427 return mTouchHandler.onTouchEvent(ev); 428 } 429 430 @Override 431 public void onDrawForeground(Canvas canvas) { 432 super.onDrawForeground(canvas); 433 434 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 435 for (int i = visDockStates.size() - 1; i >= 0; i--) { 436 visDockStates.get(i).viewState.draw(canvas); 437 } 438 } 439 440 @Override 441 protected boolean verifyDrawable(Drawable who) { 442 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 443 for (int i = visDockStates.size() - 1; i >= 0; i--) { 444 Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; 445 if (d == who) { 446 return true; 447 } 448 } 449 return super.verifyDrawable(who); 450 } 451 452 /**** EventBus Events ****/ 453 454 public final void onBusEvent(LaunchTaskEvent event) { 455 mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); 456 mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView, 457 event.taskView, event.screenPinningRequested, event.targetTaskBounds, 458 event.targetTaskStack); 459 } 460 461 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { 462 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION; 463 if (RecentsDebugFlags.Static.EnableStackActionButton) { 464 // Hide the stack action button 465 hideStackActionButton(taskViewExitToHomeDuration, false /* translate */); 466 } 467 animateBackgroundScrim(0f, taskViewExitToHomeDuration); 468 } 469 470 public final void onBusEvent(DragStartEvent event) { 471 updateVisibleDockRegions(Recents.getConfiguration().getDockStatesForCurrentOrientation(), 472 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 473 TaskStack.DockState.NONE.viewState.hintTextAlpha, 474 true /* animateAlpha */, false /* animateBounds */); 475 476 // Temporarily hide the stack action button without changing visibility 477 if (mStackActionButton != null) { 478 mStackActionButton.animate() 479 .alpha(0f) 480 .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION) 481 .setInterpolator(Interpolators.ALPHA_OUT) 482 .start(); 483 } 484 } 485 486 public final void onBusEvent(DragDropTargetChangedEvent event) { 487 if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { 488 updateVisibleDockRegions( 489 Recents.getConfiguration().getDockStatesForCurrentOrientation(), 490 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 491 TaskStack.DockState.NONE.viewState.hintTextAlpha, 492 true /* animateAlpha */, true /* animateBounds */); 493 } else { 494 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 495 updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, 496 false /* isDefaultDockState */, -1, -1, true /* animateAlpha */, 497 true /* animateBounds */); 498 } 499 if (mStackActionButton != null) { 500 event.addPostAnimationCallback(new Runnable() { 501 @Override 502 public void run() { 503 // Move the clear all button to its new position 504 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); 505 mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top, 506 buttonBounds.right, buttonBounds.bottom); 507 } 508 }); 509 } 510 } 511 512 public final void onBusEvent(final DragEndEvent event) { 513 // Handle the case where we drop onto a dock region 514 if (event.dropTarget instanceof TaskStack.DockState) { 515 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 516 517 // Hide the dock region 518 updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1, 519 false /* animateAlpha */, false /* animateBounds */); 520 521 // We translated the view but we need to animate it back from the current layout-space 522 // rect to its final layout-space rect 523 Utilities.setViewFrameFromTranslation(event.taskView); 524 525 // Dock the task and launch it 526 SystemServicesProxy ssp = Recents.getSystemServices(); 527 if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) { 528 final OnAnimationStartedListener startedListener = 529 new OnAnimationStartedListener() { 530 @Override 531 public void onAnimationStarted() { 532 EventBus.getDefault().send(new DockedFirstAnimationFrameEvent()); 533 // Remove the task and don't bother relaying out, as all the tasks will be 534 // relaid out when the stack changes on the multiwindow change event 535 getStack().removeTask(event.task, null, true /* fromDockGesture */); 536 } 537 }; 538 539 final Rect taskRect = getTaskRect(event.taskView); 540 IAppTransitionAnimationSpecsFuture future = 541 mTransitionHelper.getAppTransitionFuture( 542 new AnimationSpecComposer() { 543 @Override 544 public List<AppTransitionAnimationSpec> composeSpecs() { 545 return mTransitionHelper.composeDockAnimationSpec( 546 event.taskView, taskRect); 547 } 548 }); 549 ssp.overridePendingAppTransitionMultiThumbFuture(future, 550 mTransitionHelper.wrapStartedListener(startedListener), 551 true /* scaleUp */); 552 553 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, 554 event.task.getTopComponent().flattenToShortString()); 555 } else { 556 EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task, 557 event.taskView)); 558 } 559 } else { 560 // Animate the overlay alpha back to 0 561 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, 562 true /* animateAlpha */, false /* animateBounds */); 563 } 564 565 // Show the stack action button again without changing visibility 566 if (mStackActionButton != null) { 567 mStackActionButton.animate() 568 .alpha(1f) 569 .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION) 570 .setInterpolator(Interpolators.ALPHA_IN) 571 .start(); 572 } 573 } 574 575 public final void onBusEvent(final DragEndCancelledEvent event) { 576 // Animate the overlay alpha back to 0 577 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, 578 true /* animateAlpha */, false /* animateBounds */); 579 } 580 581 private Rect getTaskRect(TaskView taskView) { 582 int[] location = taskView.getLocationOnScreen(); 583 int viewX = location[0]; 584 int viewY = location[1]; 585 return new Rect(viewX, viewY, 586 (int) (viewX + taskView.getWidth() * taskView.getScaleX()), 587 (int) (viewY + taskView.getHeight() * taskView.getScaleY())); 588 } 589 590 public final void onBusEvent(DraggingInRecentsEvent event) { 591 if (mTaskStackView.getTaskViews().size() > 0) { 592 setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY()); 593 } 594 } 595 596 public final void onBusEvent(DraggingInRecentsEndedEvent event) { 597 ViewPropertyAnimator animator = animate(); 598 if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 599 animator.translationY(getHeight()); 600 animator.withEndAction(new Runnable() { 601 @Override 602 public void run() { 603 WindowManagerProxy.getInstance().maximizeDockedStack(); 604 } 605 }); 606 mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity); 607 } else { 608 animator.translationY(0f); 609 animator.setListener(null); 610 mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity); 611 } 612 animator.start(); 613 } 614 615 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) { 616 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 617 if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp 618 && getStack().getTaskCount() > 0) { 619 animateBackgroundScrim(1f, 620 TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION); 621 } 622 } 623 624 public final void onBusEvent(AllTaskViewsDismissedEvent event) { 625 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); 626 } 627 628 public final void onBusEvent(DismissAllTaskViewsEvent event) { 629 SystemServicesProxy ssp = Recents.getSystemServices(); 630 if (!ssp.hasDockedTask()) { 631 // Animate the background away only if we are dismissing Recents to home 632 animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION); 633 } 634 } 635 636 public final void onBusEvent(ShowStackActionButtonEvent event) { 637 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 638 return; 639 } 640 641 showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate); 642 } 643 644 public final void onBusEvent(HideStackActionButtonEvent event) { 645 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 646 return; 647 } 648 649 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); 650 } 651 652 public final void onBusEvent(MultiWindowStateChangedEvent event) { 653 updateStack(event.stack, false /* setStackViewTasks */); 654 } 655 656 /** 657 * Shows the stack action button. 658 */ 659 private void showStackActionButton(final int duration, final boolean translate) { 660 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 661 return; 662 } 663 664 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 665 if (mStackActionButton.getVisibility() == View.INVISIBLE) { 666 mStackActionButton.setVisibility(View.VISIBLE); 667 mStackActionButton.setAlpha(0f); 668 if (translate) { 669 mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f); 670 } else { 671 mStackActionButton.setTranslationY(0f); 672 } 673 postAnimationTrigger.addLastDecrementRunnable(new Runnable() { 674 @Override 675 public void run() { 676 if (translate) { 677 mStackActionButton.animate() 678 .translationY(0f); 679 } 680 mStackActionButton.animate() 681 .alpha(1f) 682 .setDuration(duration) 683 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 684 .start(); 685 } 686 }); 687 } 688 postAnimationTrigger.flushLastDecrementRunnables(); 689 } 690 691 /** 692 * Hides the stack action button. 693 */ 694 private void hideStackActionButton(int duration, boolean translate) { 695 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 696 return; 697 } 698 699 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 700 hideStackActionButton(duration, translate, postAnimationTrigger); 701 postAnimationTrigger.flushLastDecrementRunnables(); 702 } 703 704 /** 705 * Hides the stack action button. 706 */ 707 private void hideStackActionButton(int duration, boolean translate, 708 final ReferenceCountedTrigger postAnimationTrigger) { 709 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 710 return; 711 } 712 713 if (mStackActionButton.getVisibility() == View.VISIBLE) { 714 if (translate) { 715 mStackActionButton.animate() 716 .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f); 717 } 718 mStackActionButton.animate() 719 .alpha(0f) 720 .setDuration(duration) 721 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 722 .withEndAction(new Runnable() { 723 @Override 724 public void run() { 725 mStackActionButton.setVisibility(View.INVISIBLE); 726 postAnimationTrigger.decrement(); 727 } 728 }) 729 .start(); 730 postAnimationTrigger.increment(); 731 } 732 } 733 734 /** 735 * Updates the dock region to match the specified dock state. 736 */ 737 private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, 738 boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha, 739 boolean animateAlpha, boolean animateBounds) { 740 ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates, 741 new ArraySet<TaskStack.DockState>()); 742 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 743 for (int i = visDockStates.size() - 1; i >= 0; i--) { 744 TaskStack.DockState dockState = visDockStates.get(i); 745 TaskStack.DockState.ViewState viewState = dockState.viewState; 746 if (newDockStates == null || !newDockStatesSet.contains(dockState)) { 747 // This is no longer visible, so hide it 748 viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION, 749 Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds); 750 } else { 751 // This state is now visible, update the bounds and show it 752 int areaAlpha = overrideAreaAlpha != -1 753 ? overrideAreaAlpha 754 : viewState.dockAreaAlpha; 755 int hintAlpha = overrideHintAlpha != -1 756 ? overrideHintAlpha 757 : viewState.hintTextAlpha; 758 Rect bounds = isDefaultDockState 759 ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(), 760 mSystemInsets) 761 : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(), 762 mDividerSize, mSystemInsets, getResources()); 763 if (viewState.dockAreaOverlay.getCallback() != this) { 764 viewState.dockAreaOverlay.setCallback(this); 765 viewState.dockAreaOverlay.setBounds(bounds); 766 } 767 viewState.startAnimation(bounds, areaAlpha, hintAlpha, 768 TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN, 769 animateAlpha, animateBounds); 770 } 771 } 772 } 773 774 /** 775 * Animates the background scrim to the given {@param alpha}. 776 */ 777 private void animateBackgroundScrim(float alpha, int duration) { 778 Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator); 779 // Calculate the absolute alpha to animate from 780 int fromAlpha = mBackgroundScrim.getAlpha(); 781 int toAlpha = (int) (alpha * mScrimAlpha * 255); 782 mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA, 783 fromAlpha, toAlpha); 784 mBackgroundScrimAnimator.setDuration(duration); 785 mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha 786 ? Interpolators.ALPHA_IN 787 : Interpolators.ALPHA_OUT); 788 mBackgroundScrimAnimator.start(); 789 } 790 791 /** 792 * @return the bounds of the stack action button. 793 */ 794 private Rect getStackActionButtonBoundsFromStackLayout() { 795 Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect()); 796 int left = isLayoutRtl() 797 ? actionButtonRect.left - mStackActionButton.getPaddingLeft() 798 : actionButtonRect.right + mStackActionButton.getPaddingRight() 799 - mStackActionButton.getMeasuredWidth(); 800 int top = actionButtonRect.top + 801 (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2; 802 actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(), 803 top + mStackActionButton.getMeasuredHeight()); 804 return actionButtonRect; 805 } 806 807 public void dump(String prefix, PrintWriter writer) { 808 String innerPrefix = prefix + " "; 809 String id = Integer.toHexString(System.identityHashCode(this)); 810 811 writer.print(prefix); writer.print(TAG); 812 writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N"); 813 writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets)); 814 writer.print(" [0x"); writer.print(id); writer.print("]"); 815 writer.println(); 816 817 if (getStack() != null) { 818 getStack().dump(innerPrefix, writer); 819 } 820 if (mTaskStackView != null) { 821 mTaskStackView.dump(innerPrefix, writer); 822 } 823 } 824 825 @Override 826 public void onColorsChanged(ColorExtractor.GradientColors colors, int which) { 827 if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { 828 mBackgroundScrim.setColors(colors); 829 } 830 } 831} 832