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