From d2a030613154e2007d9816e090c39e9726e7adba Mon Sep 17 00:00:00 2001 From: Winson Date: Fri, 15 Apr 2016 11:19:07 -0700 Subject: [PATCH 1/6] Adding desired undocking animation. - Separating task binding from task data updating - Removing unused parameters Bug: 27154882 Change-Id: Ia970597d57857a74b5d008e91a93703d8fb70c1e --- .../layout/recents_stack_action_button.xml | 3 +- .../systemui/recents/RecentsActivity.java | 10 +- .../android/systemui/recents/RecentsImpl.java | 3 +- .../MultiWindowStateChangedEvent.java | 9 +- .../recents/model/RecentsTaskLoader.java | 18 +--- .../systemui/recents/views/RecentsView.java | 12 ++- .../recents/views/SystemBarScrimViews.java | 2 +- .../views/TaskStackAnimationHelper.java | 100 +++++++++++++++++- .../views/TaskStackLayoutAlgorithm.java | 10 +- .../systemui/recents/views/TaskStackView.java | 84 ++++++--------- .../views/TaskStackViewTouchHandler.java | 2 +- .../systemui/recents/views/TaskView.java | 18 ++-- .../recents/views/TaskViewHeader.java | 21 ++-- .../recents/views/TaskViewThumbnail.java | 24 +++-- 14 files changed, 201 insertions(+), 115 deletions(-) diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml index 625e9c111672d..43b3de1a0244a 100644 --- a/packages/SystemUI/res/layout/recents_stack_action_button.xml +++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml @@ -32,4 +32,5 @@ android:shadowRadius="5" android:fontFamily="sans-serif-medium" android:background="?android:selectableItemBackground" - android:visibility="invisible" /> + android:visibility="invisible" + android:forceHasOverlappingRendering="false" /> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 4d69280dfa220..a58e12e169dc4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -349,7 +349,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD loader.loadTasks(this, loadPlan, loadOpts); TaskStack stack = loadPlan.getTaskStack(); mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0); - mRecentsView.updateStack(stack); + mRecentsView.updateStack(stack, true /* setStackViewTasks */); // Update the nav bar scrim, but defer the animation until the enter-window event boolean animateNavBarScrim = !launchState.launchedViaDockGesture; @@ -455,13 +455,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */, false /* fromDeviceOrientationChange */, numStackTasks > 0)); - - if (mRecentsView != null) { - mRecentsView.updateStack(stack); - } - - EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, - numStackTasks > 0)); + EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, stack)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 0413bc9fcc4e6..e192da7bb5471 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -771,8 +771,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (icon != null) { icon.setCallback(null); } - mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */, + mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */, disabledInSafeMode); + mHeaderBar.onTaskDataLoaded(); mHeaderBar.setDimAlpha(toTransform.dimAlpha); mHeaderBar.draw(c); c.setBitmap(null); diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java index cf2a68e2cf955..11649fbbb6a68 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java @@ -17,17 +17,18 @@ package com.android.systemui.recents.events.activity; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.model.TaskStack; /** * This is sent by the activity whenever the multi-window state has changed. */ -public class MultiWindowStateChangedEvent extends EventBus.Event { +public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent { public final boolean inMultiWindow; - public final boolean hasStackTasks; + public final TaskStack stack; - public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) { + public MultiWindowStateChangedEvent(boolean inMultiWindow, TaskStack stack) { this.inMultiWindow = inMultiWindow; - this.hasStackTasks = hasStackTasks; + this.stack = stack; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 82c81ae3e5d3c..fb92971703d58 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -353,23 +353,15 @@ public class RecentsTaskLoader { /** * Acquires the task resource data directly from the cache, loading if necessary. - * - * @param fetchAndInvalidateThumbnails If set, will try loading thumbnails, invalidating them - * in the cache and loading if necessary. Otherwise, do not - * load the thumbnail unless the icon also has to be loaded. */ - public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) { + public void loadTaskData(Task t) { Drawable icon = mIconCache.getAndInvalidateIfModified(t.key); Bitmap thumbnail = null; ActivityManager.TaskThumbnailInfo thumbnailInfo = null; - if (fetchAndInvalidateThumbnails) { - ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key); - if (thumbnailData != null) { - thumbnail = thumbnailData.thumbnail; - thumbnailInfo = thumbnailData.thumbnailInfo; - } - } else { - thumbnail = mDefaultThumbnail; + ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key); + if (thumbnailData != null) { + thumbnail = thumbnailData.thumbnail; + thumbnailInfo = thumbnailData.thumbnailInfo; } // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index d55c7d80f0ee5..6604cccdb93dd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -57,6 +57,7 @@ import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEve import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; import com.android.systemui.recents.events.activity.LaunchTaskEvent; +import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; @@ -143,7 +144,6 @@ public class RecentsView extends FrameLayout { R.dimen.recents_task_view_rounded_corners_radius); mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button, this, false); - mStackActionButton.forceHasOverlappingRendering(false); mStackActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -203,9 +203,11 @@ public class RecentsView extends FrameLayout { /** * Called from RecentsActivity when the task stack is updated. */ - public void updateStack(TaskStack stack) { + public void updateStack(TaskStack stack, boolean setStackViewTasks) { mStack = stack; - mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */); + if (setStackViewTasks) { + mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */); + } // Update the top level view's visibilities if (stack.getTaskCount() > 0) { @@ -622,6 +624,10 @@ public class RecentsView extends FrameLayout { hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); } + public final void onBusEvent(MultiWindowStateChangedEvent event) { + updateStack(event.stack, false /* setStackViewTasks */); + } + /** * Shows the stack action button. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index dce2353187338..06a2c1e4af786 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -146,7 +146,7 @@ public class SystemBarScrimViews { } public final void onBusEvent(MultiWindowStateChangedEvent event) { - animateScrimToCurrentNavBarState(event.hasStackTasks); + animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java index 665d9ad940028..e79306f55e3dc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java @@ -485,7 +485,7 @@ public class TaskStackAnimationHelper { // Get the final set of task transforms mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks, - mTmpFinalTaskTransforms); + true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms); // Focus the task view TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask); @@ -529,7 +529,7 @@ public class TaskStackAnimationHelper { int duration; Interpolator interpolator; if (willScrollToFront) { - duration = Math.max(100, 100 + ((i - 1) * 50)); + duration = calculateStaggeredAnimDuration(i); interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR; } else { if (i < newFocusTaskViewIndex) { @@ -553,4 +553,100 @@ public class TaskStackAnimationHelper { } return willScroll; } + + /** + * Starts the animation to go to the initial stack layout with a task focused. In addition, the + * previous task will be animated in after the scroll completes. + */ + public void startNewStackScrollAnimation(TaskStack newStack, + ReferenceCountedTrigger animationTrigger) { + TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm(); + TaskStackViewScroller stackScroller = mStackView.getScroller(); + + // Get the current set of task transforms + ArrayList stackTasks = newStack.getStackTasks(); + mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms); + + // Update the stack + mStackView.setTasks(newStack, false /* allowNotifyStackChanges */); + mStackView.updateLayoutAlgorithm(false /* boundScroll */); + + // Pick up the newly visible views after the scroll + final float newScroll = stackLayout.mInitialScrollP; + mStackView.bindVisibleTaskViews(newScroll); + + // Update the internal state + stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED); + stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */); + stackScroller.setStackScroll(newScroll); + mStackView.cancelDeferredTaskViewLayoutAnimation(); + + // Get the final set of task transforms + mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks, + false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms); + + // Hide the front most task view until the scroll is complete + Task frontMostTask = newStack.getStackFrontMostTask(false /* includeFreeform */); + final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask); + final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get( + stackTasks.indexOf(frontMostTask)); + if (frontMostTaskView != null) { + mStackView.updateTaskViewToTransform(frontMostTaskView, + stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE); + } + + // Setup the end listener to return all the hidden views to the view pool after the + // focus animation + animationTrigger.addLastDecrementRunnable(new Runnable() { + @Override + public void run() { + mStackView.bindVisibleTaskViews(newScroll); + + // Now, animate in the front-most task + if (frontMostTaskView != null) { + mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform, + new AnimationProps(75, 200, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR)); + } + } + }); + + List taskViews = mStackView.getTaskViews(); + int taskViewCount = taskViews.size(); + for (int i = 0; i < taskViewCount; i++) { + TaskView tv = taskViews.get(i); + Task task = tv.getTask(); + + if (mStackView.isIgnoredTask(task)) { + continue; + } + if (task == frontMostTask && frontMostTaskView != null) { + continue; + } + + int taskIndex = stackTasks.indexOf(task); + TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex); + TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex); + + // Update the task to the initial state (for the newly picked up tasks) + mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE); + + int duration = calculateStaggeredAnimDuration(i); + Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR; + + AnimationProps anim = new AnimationProps() + .setDuration(AnimationProps.BOUNDS, duration) + .setInterpolator(AnimationProps.BOUNDS, interpolator) + .setListener(animationTrigger.decrementOnAnimationEnd()); + animationTrigger.increment(); + mStackView.updateTaskViewToTransform(tv, toTransform, anim); + } + } + + /** + * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and + * {@link #startNewStackScrollAnimation}. + */ + private int calculateStaggeredAnimDuration(int i) { + return Math.max(100, 100 + ((i - 1) * 50)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 34d6bcecdee67..57c2bc01e61ff 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -577,7 +577,7 @@ public class TaskStackLayoutAlgorithm { /** * Creates task overrides to ensure the initial stack layout if necessary. */ - public void setTaskOverridesForInitialState(TaskStack stack) { + public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) { RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); mTaskIndexOverrideMap.clear(); @@ -585,7 +585,7 @@ public class TaskStackLayoutAlgorithm { boolean scrollToFront = launchState.launchedFromHome || launchState.launchedViaDockGesture; if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) { - if (!launchState.launchedWithAltTab && !scrollToFront) { + if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) { // Set the initial scroll to the predefined state (which differs from the stack) float [] initialNormX = new float[] { getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset, @@ -938,7 +938,11 @@ public class TaskStackLayoutAlgorithm { * stack. */ float getStackScrollForTask(Task t) { - return mTaskIndexOverrideMap.get(t.key.id, (float) mTaskIndexMap.get(t.key.id, 0)); + Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null); + if (overrideP == null) { + return (float) mTaskIndexMap.get(t.key.id, 0); + } + return overrideP; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index a75d1e1339e29..068596bba54d2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -328,6 +328,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) { boolean isInitialized = mLayoutAlgorithm.isInitialized(); + // Only notify if we are already initialized, otherwise, everything will pick up all the // new and old tasks when we next layout mStack.setTasks(getContext(), stack.computeAllTasksList(), @@ -344,7 +345,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ public void updateToInitialState() { mStackScroller.setStackScrollToInitialState(); - mLayoutAlgorithm.setTaskOverridesForInitialState(mStack); + mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */); } /** Updates the list of task views */ @@ -508,11 +509,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * Binds the visible {@link TaskView}s at the given target scroll. */ void bindVisibleTaskViews(float targetStackScroll) { - bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */); - } - - void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) { - bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides); + bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */); } /** @@ -525,17 +522,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s * includes those visible at the current stack scroll, and all at the * target stack scroll. - * @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible - * {@link TaskView}s * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for * tasks at their non-overridden task progress */ - void bindVisibleTaskViews(float targetStackScroll, ArraySet ignoreTasksSet, - boolean ignoreTaskOverrides) { + void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) { // Get all the task transforms ArrayList tasks = mStack.getStackTasks(); int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks, - mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet, + mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks, ignoreTaskOverrides); // Return all the invisible children to the pool @@ -548,7 +542,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Task task = tv.getTask(); // Skip ignored tasks - if (ignoreTasksSet.contains(task.key)) { + if (mIgnoreTasks.contains(task.key)) { continue; } @@ -578,7 +572,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskViewTransform transform = mCurrentTaskTransforms.get(i); // Skip ignored tasks - if (ignoreTasksSet.contains(task.key)) { + if (mIgnoreTasks.contains(task.key)) { continue; } @@ -626,10 +620,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } /** - * @see #relayoutTaskViews(AnimationProps, ArraySet, boolean) + * @see #relayoutTaskViews(AnimationProps, boolean) */ public void relayoutTaskViews(AnimationProps animation) { - relayoutTaskViews(animation, mIgnoreTasks, false /* ignoreTaskOverrides */); + relayoutTaskViews(animation, false /* ignoreTaskOverrides */); } /** @@ -637,16 +631,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any * animations that are current running on those task views, and will ensure that the children * {@link TaskView}s will match the set of visible tasks in the stack. - * - * @param ignoreTasksSet the set of tasks to ignore in the relayout */ - private void relayoutTaskViews(AnimationProps animation, ArraySet ignoreTasksSet, - boolean ignoreTaskOverrides) { + private void relayoutTaskViews(AnimationProps animation, boolean ignoreTaskOverrides) { // If we had a deferred animation, cancel that mDeferredTaskViewLayoutAnimation = null; // Synchronize the current set of TaskViews - bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet, + bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides /* ignoreTaskOverrides */); // Animate them to their final transforms with the given animation @@ -657,7 +648,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int taskIndex = mStack.indexOfStackTask(tv.getTask()); TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex); - if (ignoreTasksSet.contains(tv.getTask().key)) { + if (mIgnoreTasks.contains(tv.getTask().key)) { continue; } @@ -715,13 +706,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * {@param stackScroll} and {@param focusState}. */ public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList tasks, - ArrayList transformsOut) { + boolean ignoreTaskOverrides, ArrayList transformsOut) { Utilities.matchTaskListSize(tasks, transformsOut); for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); TaskViewTransform transform = transformsOut.get(i); mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null, - true /* forceUpdate */, true /* ignoreTaskOverrides */); + true /* forceUpdate */, ignoreTaskOverrides); transform.visible = true; } } @@ -812,22 +803,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * Updates the layout algorithm min and max virtual scroll bounds. - * - * @see #updateLayoutAlgorithm(boolean, ArraySet) */ public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) { - updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks); - } - - /** - * Updates the min and max virtual scroll bounds. - * - * @param ignoreTasksSet the set of tasks to ignore in the relayout - */ - private void updateLayoutAlgorithm(boolean boundScrollToNewMinMax, - ArraySet ignoreTasksSet) { // Compute the min and max scroll values - mLayoutAlgorithm.update(mStack, ignoreTasksSet); + mLayoutAlgorithm.update(mStack, mIgnoreTasks); // Update the freeform workspace background SystemServicesProxy ssp = Recents.getSystemServices(); @@ -1195,8 +1174,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Rebind all the views, including the ignore ones - bindVisibleTaskViews(mStackScroller.getStackScroll(), mIgnoreTasks, - false /* ignoreTaskOverrides */); + bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */); // Measure each of the TaskViews mTmpTaskViews.clear(); @@ -1553,7 +1531,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.onTaskBound(task); // Load the task data - Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */); + Recents.getTaskLoader().loadTaskData(task); } private void unbindTaskView(TaskView tv, Task task) { @@ -1834,7 +1812,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal updateLayoutAlgorithm(true /* boundScroll */); addIgnoreTask(event.task); } - relayoutTaskViews(animation, mIgnoreTasks, ignoreTaskOverrides); + relayoutTaskViews(animation, ignoreTaskOverrides); } public final void onBusEvent(final DragEndEvent event) { @@ -1948,26 +1926,24 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - public final void onBusEvent(MultiWindowStateChangedEvent event) { - if (!event.inMultiWindow) { + public final void onBusEvent(final MultiWindowStateChangedEvent event) { + if (event.inMultiWindow) { + setTasks(event.stack, true /* allowNotifyStackChanges */); + } else { + // Reset the launch state before handling the multiwindow change + RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); + launchState.reset(); + // Defer until the next frame to ensure that we have received all the system insets, and // initial layout updates + event.getAnimationTrigger().increment(); post(new Runnable() { @Override public void run() { // Scroll the stack to the front to see the undocked task - mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() { - @Override - public void run() { - List taskViews = getTaskViews(); - int taskViewCount = taskViews.size(); - for (int i = 0; i < taskViewCount; i++) { - TaskView tv = taskViews.get(i); - tv.getHeaderView().rebindToTask(tv.getTask(), - tv.mTouchExplorationEnabled, tv.mIsDisabledInSafeMode); - } - } - }); + mAnimationHelper.startNewStackScrollAnimation(event.stack, + event.getAnimationTrigger()); + event.getAnimationTrigger().decrement(); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 3cdb1fb27854c..4441ddcca7777 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -483,7 +483,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // Get the final set of task transforms (with task removed) mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED, - mCurrentTasks, mFinalTaskTransforms); + mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms); // Set the target to scroll towards upon dismissal mTargetStackScroll = newStackScroll; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 37f5a9fc99747..7ea70b587fd70 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -24,13 +24,9 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Outline; -import android.graphics.Paint; import android.graphics.Point; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.util.AttributeSet; import android.util.FloatProperty; @@ -607,6 +603,8 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks mTask = t; mTask.addCallback(this); mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode(); + mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode); + mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); if (!t.isDockable && ssp.hasDockedTask()) { if (mIncompatibleAppToastView == null) { @@ -623,15 +621,15 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks @Override public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) { - // Bind each of the views to the new task data - mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode); - mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); + // Update each of the views to the new task data + mThumbnailView.onTaskDataLoaded(thumbnailInfo); + mHeaderView.onTaskDataLoaded(); mTaskDataLoaded = true; } @Override public void onTaskDataUnloaded() { - // Unbind each of the views from the task data and remove the task callback + // Unbind each of the views from the task and remove the task callback mTask.removeCallback(this); mThumbnailView.unbindFromTask(); mHeaderView.unbindFromTask(mTouchExplorationEnabled); @@ -640,7 +638,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks @Override public void onTaskStackIdChanged() { - mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); + // Force rebind the header, the thumbnail does not change due to stack changes + mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); + mHeaderView.onTaskDataLoaded(); } /**** View.OnClickListener Implementation ****/ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index fb0fc3076f304..aac6d13706a74 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -438,21 +438,18 @@ public class TaskViewHeader extends FrameLayout } } - /** Binds the bar view to the task */ - public void rebindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) { + /** + * Binds the bar view to the task. + */ + public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) { mTask = t; - // If an activity icon is defined, then we use that as the primary icon to show in the bar, - // otherwise, we fall back to the application icon int primaryColor = disabledInSafeMode ? mDisabledTaskBarBackgroundColor : t.colorPrimary; if (mBackground.getColor() != primaryColor) { updateBackgroundColor(primaryColor, mDimAlpha); } - if (t.icon != null) { - mIconView.setImageDrawable(t.icon); - } if (!mTitleView.getText().toString().equals(t.title)) { mTitleView.setText(t.title); } @@ -497,6 +494,16 @@ public class TaskViewHeader extends FrameLayout } } + /** + * Called when the bound task's data has loaded and this view should update to reflect the + * changes. + */ + public void onTaskDataLoaded() { + if (mTask.icon != null) { + mIconView.setImageDrawable(mTask.icon); + } + } + /** Unbinds the bar view from the task */ void unbindFromTask(boolean touchExplorationEnabled) { mTask = null; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 4de7713eb2fb1..8977f504209f9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -302,21 +302,29 @@ public class TaskViewThumbnail extends View { updateThumbnailPaintFilter(); } - /** Binds the thumbnail view to the task */ - void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo, - boolean disabledInSafeMode) { + /** + * Binds the thumbnail view to the task. + */ + void bindToTask(Task t, boolean disabledInSafeMode) { mTask = t; mDisabledInSafeMode = disabledInSafeMode; - if (t.thumbnail != null) { - setThumbnail(t.thumbnail, thumbnailInfo); - } else { - setThumbnail(null, null); - } if (t.colorBackground != 0) { mBgFillPaint.setColor(t.colorBackground); } } + /** + * Called when the bound task's data has loaded and this view should update to reflect the + * changes. + */ + void onTaskDataLoaded(ActivityManager.TaskThumbnailInfo thumbnailInfo) { + if (mTask.thumbnail != null) { + setThumbnail(mTask.thumbnail, thumbnailInfo); + } else { + setThumbnail(null, null); + } + } + /** Unbinds the thumbnail view from the task */ void unbindFromTask() { mTask = null; From 19ecc8ce2d8ca5d27e8098521c9668b7882d271c Mon Sep 17 00:00:00 2001 From: Winson Date: Fri, 15 Apr 2016 17:54:52 -0700 Subject: [PATCH 2/6] Fixing issue with gap between task views. - Now that we changed the way the landscape tasks clipped, we no longer have to worry about the aspect ratio scaled height. Bug: 27921659 Change-Id: I86b10805e92decb9995caa3790649f9f7d211784 --- .../systemui/recents/views/TaskStackLayoutAlgorithm.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 57c2bc01e61ff..ee3a9b9e3c7d3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -454,11 +454,8 @@ public class TaskStackLayoutAlgorithm { mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin, mStackRect.right, mStackRect.top + mFocusedTopPeekHeight); - // Anchor the task rect top aligned to the non-freeform stack rect - float aspect = (float) (windowRect.width() - (mSystemInsets.left + mSystemInsets.right)) / - (windowRect.height() - (mSystemInsets.top + mSystemInsets.bottom)); - int minHeight = mStackRect.height() - mInitialTopOffset - mStackBottomOffset; - int height = (int) Math.min(mStackRect.width() / aspect, minHeight); + // Anchor the task rect top aligned to the stack rect + int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset; mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height); // Short circuit here if the stack rects haven't changed so we don't do all the work below From bde852dbf5ba8c423da85aedfe6012ca67ee497c Mon Sep 17 00:00:00 2001 From: Winson Date: Fri, 15 Apr 2016 19:06:54 -0700 Subject: [PATCH 3/6] Making the dismiss animation feel smoother. Bug: 27154882 Change-Id: Idbabfe396bc75b598861fd12b78c846f08243a90 --- .../src/com/android/systemui/SwipeHelper.java | 43 +++++++++++-------- .../views/TaskStackViewTouchHandler.java | 27 +++++++----- .../stack/NotificationStackScrollLayout.java | 2 +- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index e83819179c244..9a6fa9c6bae64 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -51,11 +51,9 @@ public class SwipeHelper implements Gefingerpoken { private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms - private int MAX_DISMISS_VELOCITY = 2000; // dp/sec + private int MAX_DISMISS_VELOCITY = 4000; // dp/sec private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms - public static float SWIPE_PROGRESS_FADE_START = 0f; // fraction of thumbnail width - // where fade starts static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width // beyond which swipe progress->0 private float mMinSwipeProgress = 0f; @@ -102,8 +100,7 @@ public class SwipeHelper implements Gefingerpoken { mFalsingThreshold = context.getResources().getDimensionPixelSize( R.dimen.swipe_helper_falsing_threshold); mFalsingManager = FalsingManager.getInstance(context); - mFlingAnimationUtils = new FlingAnimationUtils(context, - MAX_ESCAPE_ANIMATION_DURATION / 1000f /* maxLengthSeconds */); + mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f); } public void setLongPressListener(LongPressListener listener) { @@ -183,21 +180,23 @@ public class SwipeHelper implements Gefingerpoken { mMaxSwipeProgress = maxSwipeProgress; } - private float getSwipeProgressForOffset(View view) { + private float getSwipeProgressForOffset(View view, float translation) { float viewSize = getSize(view); - final float fadeSize = SWIPE_PROGRESS_FADE_END * viewSize; - float result = 1.0f; - float pos = getTranslation(view); - if (pos >= viewSize * SWIPE_PROGRESS_FADE_START) { - result = 1.0f - (pos - viewSize * SWIPE_PROGRESS_FADE_START) / fadeSize; - } else if (pos < viewSize * (1.0f - SWIPE_PROGRESS_FADE_START)) { - result = 1.0f + (viewSize * SWIPE_PROGRESS_FADE_START + pos) / fadeSize; - } + float result = Math.abs(translation / viewSize); return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress); } + private float getSwipeAlpha(float progress) { + return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END)); + } + private void updateSwipeProgressFromOffset(View animView, boolean dismissable) { - float swipeProgress = getSwipeProgressForOffset(animView); + updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView)); + } + + private void updateSwipeProgressFromOffset(View animView, boolean dismissable, + float translation) { + float swipeProgress = getSwipeProgressForOffset(animView, translation); if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) { if (FADE_OUT_DURING_SWIPE && dismissable) { float alpha = swipeProgress; @@ -208,7 +207,7 @@ public class SwipeHelper implements Gefingerpoken { animView.setLayerType(View.LAYER_TYPE_NONE, null); } } - animView.setAlpha(getSwipeProgressForOffset(animView)); + animView.setAlpha(getSwipeAlpha(swipeProgress)); } } invalidateGlobalRegion(animView); @@ -485,7 +484,7 @@ public class SwipeHelper implements Gefingerpoken { * view is being animated to dismiss or snap. */ public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) { - updateSwipeProgressFromOffset(animView, canBeDismissed); + updateSwipeProgressFromOffset(animView, canBeDismissed, value); } private void snapChildInstantly(final View view) { @@ -600,7 +599,15 @@ public class SwipeHelper implements Gefingerpoken { } protected float getEscapeVelocity() { - return SWIPE_ESCAPE_VELOCITY * mDensityScale; + return getUnscaledEscapeVelocity() * mDensityScale; + } + + protected float getUnscaledEscapeVelocity() { + return SWIPE_ESCAPE_VELOCITY; + } + + protected long getMaxEscapeAnimDuration() { + return MAX_ESCAPE_ANIMATION_DURATION; } protected boolean swipedFarEnough() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 4441ddcca7777..9edf9d6c66c4f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -60,8 +60,7 @@ import java.util.List; class TaskStackViewTouchHandler implements SwipeHelper.Callback { private static final int INACTIVE_POINTER_ID = -1; - private static final Interpolator STACK_TRANSFORM_INTERPOLATOR = - new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f); + private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec // The min overscroll is the amount of task progress overscroll we want / the max overscroll // curve value below private static final float MAX_OVERSCROLL = 0.7f / 0.3f; @@ -125,7 +124,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) { @Override protected float getSize(View v) { - return mSv.getWidth(); + return getScaledDismissSize(); } @Override @@ -138,6 +137,16 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mSwipeHelperAnimations.put(v, anim); } + + @Override + protected float getUnscaledEscapeVelocity() { + return CHALLENGING_SWIPE_ESCAPE_VELOCITY; + } + + @Override + protected long getMaxEscapeAnimDuration() { + return 700; + } }; mSwipeHelper.setDisableHardwareLayers(true); } @@ -500,7 +509,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { @Override public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) { - updateTaskViewTransforms(getDismissFraction(v)); + updateTaskViewTransforms(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress)); return true; } @@ -616,13 +625,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } /** - * Returns the fraction which we should interpolate the other task views based on the dismissal - * of this given task. - * - * TODO: We can interpolate this to adjust when the other tasks should respond to the dismissal + * Returns the scaled size used to calculate the dismiss fraction. */ - private float getDismissFraction(View v) { - float fraction = Math.min(1f, Math.abs(v.getTranslationX() / mSv.getWidth())); - return STACK_TRANSFORM_INTERPOLATOR.getInterpolation(fraction); + private float getScaledDismissSize() { + return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 8b52bf65a6736..4986740ebc3f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -747,7 +747,7 @@ public class NotificationStackScrollLayout extends ViewGroup public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { if (!mIsExpanded && isPinnedHeadsUp(animView) && canChildBeDismissed(animView)) { mScrimController.setTopHeadsUpDragAmount(animView, - Math.min(Math.abs(swipeProgress - 1.0f), 1.0f)); + Math.min(Math.abs(swipeProgress / 2f - 1.0f), 1.0f)); } return true; // Don't fade out the notification } From 3664426dcf3a603a52d630a74f3caef9da682457 Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 18 Apr 2016 12:57:56 -0700 Subject: [PATCH 4/6] Fixing animation spec animation problem. - We were always using the stack layout to get the task transforms, but when we are animating to a given task position (ie. hitting recents while the home->recents transition is happening), we would start the animation with the final task rect, instead of the current task rect. Instead, use the current transform of the task view, and transform that to screen coordinates and use it. Bug: 27154882 Change-Id: I25af8fad97ee78669c952dd81708e6cee5dfab05 --- .../recents/views/RecentsTransitionHelper.java | 15 +++++++-------- .../recents/views/TaskStackLayoutAlgorithm.java | 13 ++++++++++--- .../systemui/recents/views/TaskStackView.java | 1 - 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index db5413f85c241..04f10ef8e8930 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -286,10 +286,9 @@ public class RecentsTransitionHelper { // Calculate the offscreen task rect (for tasks that are not backed by views) float stackScroll = stackView.getScroller().getStackScroll(); TaskView taskView = stackView.getChildViewForTask(task); - TaskStackLayoutAlgorithm layoutAlgorithm = stackView.getStackAlgorithm(); - Rect offscreenTaskRect = new Rect(layoutAlgorithm.mTaskRect); - offscreenTaskRect.offsetTo(offscreenTaskRect.left, - layoutAlgorithm.mStackRect.bottom); + TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm(); + Rect offscreenTaskRect = new Rect(); + stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect); // If this is a full screen stack, the transition will be towards the single, full screen // task. We only need the transition spec for this task. @@ -302,8 +301,8 @@ public class RecentsTransitionHelper { if (taskView == null) { specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect)); } else { - layoutAlgorithm.getStackTransformScreenCoordinates(task, stackScroll, mTmpTransform, - null); + mTmpTransform.fillIn(taskView); + stackLayout.transformToScreenCoordinates(mTmpTransform); specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform, true /* addHeaderBitmap */)); } @@ -324,8 +323,8 @@ public class RecentsTransitionHelper { // never happen) specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect)); } else { - layoutAlgorithm.getStackTransformScreenCoordinates(t, stackScroll, - mTmpTransform, null); + mTmpTransform.fillIn(taskView); + stackLayout.transformToScreenCoordinates(mTmpTransform); specs.add(composeAnimationSpec(stackView, tv, mTmpTransform, true /* addHeaderBitmap */)); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index ee3a9b9e3c7d3..bdc4c1a12117f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -831,12 +831,19 @@ public class TaskStackLayoutAlgorithm { */ public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform) { - Rect windowRect = Recents.getSystemServices().getWindowRect(); TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, true /* forceUpdate */, false /* ignoreTaskOverrides */); - transform.rect.offset(windowRect.left, windowRect.top); - return transform; + return transformToScreenCoordinates(transform); + } + + /** + * Transforms the given {@param transformOut} to the screen coordinates. + */ + public TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut) { + Rect windowRect = Recents.getSystemServices().getWindowRect(); + transformOut.rect.offset(windowRect.left, windowRect.top); + return transformOut; } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 068596bba54d2..6176d99a7c174 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -1618,7 +1618,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } if (launchTaskIndex != -1) { // Stop all animations - mUIDozeTrigger.stopDozing(); cancelAllTaskViewAnimations(); final Task launchTask = mStack.getStackTasks().get(launchTaskIndex); From 99ef458ff1b79c93a8dfdc289fb6ff80fd7aacf6 Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 18 Apr 2016 16:57:27 -0700 Subject: [PATCH 5/6] Adding hint text for drop targets. Bug: 28192178 Change-Id: Id7be5129488622b56082d2dfa328b751311cabd7 --- packages/SystemUI/res/values/dimens.xml | 3 + packages/SystemUI/res/values/strings.xml | 2 + .../systemui/recents/model/TaskStack.java | 145 +++++++++++++++--- .../systemui/recents/views/RecentsView.java | 30 ++-- .../views/RecentsViewTouchHandler.java | 1 + 5 files changed, 143 insertions(+), 38 deletions(-) diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 03b9837e6175d..5043610085708 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -640,6 +640,9 @@ 24dp + + 14sp + 0.6 diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 37b00bbfdc27f..a03aa28c47c57 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -737,6 +737,8 @@ Clear all App doesn\'t support split screen + + Drag here to use split screen Split Horizontal diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 95e276f7c8cf8..47995c4b73f3e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -30,17 +30,21 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.RectEvaluator; +import android.annotation.IntDef; import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntProperty; import android.util.SparseArray; import android.view.animation.Interpolator; @@ -56,6 +60,8 @@ import com.android.systemui.recents.views.DropTarget; import com.android.systemui.recents.views.TaskStackLayoutAlgorithm; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -240,22 +246,30 @@ public class TaskStack { */ public static class DockState implements DropTarget { + // The rotation to apply to the hint text + @Retention(RetentionPolicy.SOURCE) + @IntDef({HORIZONTAL, VERTICAL}) + public @interface TextOrientation {} + private static final int HORIZONTAL = 0; + private static final int VERTICAL = 1; + private static final int DOCK_AREA_ALPHA = 192; - public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null); + public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL, + null, null, null); public static final DockState LEFT = new DockState(DOCKED_LEFT, - DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, + DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL, new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.5f, 1)); public static final DockState TOP = new DockState(DOCKED_TOP, - DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, + DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL, new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.5f)); public static final DockState RIGHT = new DockState(DOCKED_RIGHT, - DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, + DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL, new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1), new RectF(0.5f, 0, 1, 1)); public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM, - DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, + DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL, new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1), new RectF(0, 0.5f, 1, 1)); @@ -267,33 +281,109 @@ public class TaskStack { } // Represents the view state of this dock state - public class ViewState { + public static class ViewState { + private static final IntProperty HINT_ALPHA = + new IntProperty("drawableAlpha") { + @Override + public void setValue(ViewState object, int alpha) { + object.mHintTextAlpha = alpha; + object.dockAreaOverlay.invalidateSelf(); + } + + @Override + public Integer get(ViewState object) { + return object.mHintTextAlpha; + } + }; + public final int dockAreaAlpha; public final ColorDrawable dockAreaOverlay; - private AnimatorSet dockAreaOverlayAnimator; + public final int hintTextAlpha; + public final int hintTextOrientation; - private ViewState(int alpha) { - dockAreaAlpha = alpha; + private final int mHintTextResId; + private String mHintText; + private Paint mHintTextPaint; + private Point mHintTextBounds = new Point(); + private int mHintTextAlpha = 255; + private AnimatorSet mDockAreaOverlayAnimator; + private Rect mTmpRect = new Rect(); + + private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation, + int hintTextResId) { + dockAreaAlpha = areaAlpha; dockAreaOverlay = new ColorDrawable(0xFFffffff); dockAreaOverlay.setAlpha(0); + hintTextAlpha = hintAlpha; + hintTextOrientation = hintOrientation; + mHintTextResId = hintTextResId; + mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHintTextPaint.setColor(Color.WHITE); + } + + /** + * Updates the view state with the given context. + */ + public void update(Context context) { + Resources res = context.getResources(); + mHintText = context.getString(mHintTextResId); + mHintTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.recents_drag_hint_text_size)); + mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect); + mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height()); + } + + /** + * Draws the current view state. + */ + public void draw(Canvas canvas) { + // Draw the overlay background + if (dockAreaOverlay.getAlpha() > 0) { + dockAreaOverlay.draw(canvas); + } + + // Draw the hint text + if (mHintTextAlpha > 0) { + Rect bounds = dockAreaOverlay.getBounds(); + int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2; + int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2; + mHintTextPaint.setAlpha(mHintTextAlpha); + if (hintTextOrientation == VERTICAL) { + canvas.save(); + canvas.rotate(-90f, bounds.centerX(), bounds.centerY()); + } + canvas.drawText(mHintText, x, y, mHintTextPaint); + if (hintTextOrientation == VERTICAL) { + canvas.restore(); + } + } } /** * Creates a new bounds and alpha animation. */ - public void startAnimation(Rect bounds, int alpha, int duration, + public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration, Interpolator interpolator, boolean animateAlpha, boolean animateBounds) { - if (dockAreaOverlayAnimator != null) { - dockAreaOverlayAnimator.cancel(); + if (mDockAreaOverlayAnimator != null) { + mDockAreaOverlayAnimator.cancel(); } ArrayList animators = new ArrayList<>(); - if (dockAreaOverlay.getAlpha() != alpha) { + if (dockAreaOverlay.getAlpha() != areaAlpha) { if (animateAlpha) { animators.add(ObjectAnimator.ofInt(dockAreaOverlay, - Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha)); + Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha)); } else { - dockAreaOverlay.setAlpha(alpha); + dockAreaOverlay.setAlpha(areaAlpha); + } + } + if (mHintTextAlpha != hintAlpha) { + if (animateAlpha) { + animators.add(ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha, + hintAlpha)); + } else { + mHintTextAlpha = hintAlpha; + dockAreaOverlay.invalidateSelf(); } } if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) { @@ -307,11 +397,11 @@ public class TaskStack { } } if (!animators.isEmpty()) { - dockAreaOverlayAnimator = new AnimatorSet(); - dockAreaOverlayAnimator.playTogether(animators); - dockAreaOverlayAnimator.setDuration(duration); - dockAreaOverlayAnimator.setInterpolator(interpolator); - dockAreaOverlayAnimator.start(); + mDockAreaOverlayAnimator = new AnimatorSet(); + mDockAreaOverlayAnimator.playTogether(animators); + mDockAreaOverlayAnimator.setDuration(duration); + mDockAreaOverlayAnimator.setInterpolator(interpolator); + mDockAreaOverlayAnimator.start(); } } } @@ -331,16 +421,25 @@ public class TaskStack { * the initial touch area. This is also the new dock area to * draw. */ - DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea, + DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha, + @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea, RectF expandedTouchDockArea) { this.dockSide = dockSide; this.createMode = createMode; - this.viewState = new ViewState(dockAreaAlpha); + this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation, + R.string.recents_drag_hint_message); this.dockArea = dockArea; this.touchArea = touchArea; this.expandedTouchDockArea = expandedTouchDockArea; } + /** + * Updates the dock state with the given context. + */ + public void update(Context context) { + viewState.update(context); + } + /** * Returns whether {@param x} and {@param y} are contained in the area scaled to the * given {@param width} and {@param height}. diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 6604cccdb93dd..a5ca302bac2f0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -426,10 +426,7 @@ public class RecentsView extends FrameLayout { ArrayList visDockStates = mTouchHandler.getVisibleDockStates(); for (int i = visDockStates.size() - 1; i >= 0; i--) { - Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; - if (d.getAlpha() > 0) { - d.draw(canvas); - } + visDockStates.get(i).viewState.draw(canvas); } } @@ -465,6 +462,7 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(DragStartEvent event) { updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, + TaskStack.DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, false /* animateBounds */); } @@ -472,11 +470,12 @@ public class RecentsView extends FrameLayout { if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, + TaskStack.DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, true /* animateBounds */); } else { final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, - false /* isDefaultDockState */, -1, true /* animateAlpha */, + false /* isDefaultDockState */, -1, -1, true /* animateAlpha */, true /* animateBounds */); } if (mStackActionButton != null) { @@ -498,13 +497,9 @@ public class RecentsView extends FrameLayout { final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; // Hide the dock region - updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, + updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1, false /* animateAlpha */, false /* animateBounds */); - TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm(); - TaskStackViewScroller stackScroller = mTaskStackView.getScroller(); - TaskViewTransform tmpTransform = new TaskViewTransform(); - // We translated the view but we need to animate it back from the current layout-space // rect to its final layout-space rect int x = (int) event.taskView.getTranslationX(); @@ -548,7 +543,7 @@ public class RecentsView extends FrameLayout { event.task.getTopComponent().flattenToShortString()); } else { // Animate the overlay alpha back to 0 - updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, + updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, true /* animateAlpha */, false /* animateBounds */); } } @@ -710,8 +705,8 @@ public class RecentsView extends FrameLayout { * Updates the dock region to match the specified dock state. */ private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, - boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha, - boolean animateBounds) { + boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha, + boolean animateAlpha, boolean animateBounds) { ArraySet newDockStatesSet = Utilities.arrayToSet(newDockStates, new ArraySet()); ArrayList visDockStates = mTouchHandler.getVisibleDockStates(); @@ -720,11 +715,16 @@ public class RecentsView extends FrameLayout { TaskStack.DockState.ViewState viewState = dockState.viewState; if (newDockStates == null || !newDockStatesSet.contains(dockState)) { // This is no longer visible, so hide it - viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION, + viewState.startAnimation(null, 0, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION, Interpolators.ALPHA_OUT, animateAlpha, animateBounds); } else { // This state is now visible, update the bounds and show it - int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha); + int areaAlpha = overrideAreaAlpha != -1 + ? overrideAreaAlpha + : viewState.dockAreaAlpha; + int hintAlpha = overrideHintAlpha != -1 + ? overrideHintAlpha + : viewState.hintTextAlpha; Rect bounds = isDefaultDockState ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight()) : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(), diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java index 70c4dbdc96653..214ec90fe5ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -171,6 +171,7 @@ public class RecentsViewTouchHandler { TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation(); for (TaskStack.DockState dockState : dockStates) { registerDropTargetForCurrentDrag(dockState); + dockState.update(mRv.getContext()); mVisibleDockStates.add(dockState); } } From b677bcbee460a3246872d8090b64db1d8ec9c276 Mon Sep 17 00:00:00 2001 From: Winson Date: Mon, 18 Apr 2016 16:58:33 -0700 Subject: [PATCH 6/6] Hiding clear-all button on drag start. Bug: 27702186 Change-Id: Ibec23fdb1ce4853f52a449cbe6d8c6454f0b027c --- .../systemui/recents/views/RecentsView.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index a5ca302bac2f0..6ecd52dbccc50 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -92,7 +92,7 @@ public class RecentsView extends FrameLayout { private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200; private static final float DEFAULT_SCRIM_ALPHA = 0.33f; - private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 150; + private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134; private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100; private TaskStack mStack; @@ -464,6 +464,15 @@ public class RecentsView extends FrameLayout { true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, TaskStack.DockState.NONE.viewState.hintTextAlpha, true /* animateAlpha */, false /* animateBounds */); + + // Temporarily hide the stack action button without changing visibility + if (mStackActionButton != null) { + mStackActionButton.animate() + .alpha(0f) + .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION) + .setInterpolator(Interpolators.ALPHA_OUT) + .start(); + } } public final void onBusEvent(DragDropTargetChangedEvent event) { @@ -546,6 +555,15 @@ public class RecentsView extends FrameLayout { updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, true /* animateAlpha */, false /* animateBounds */); } + + // Show the stack action button again without changing visibility + if (mStackActionButton != null) { + mStackActionButton.animate() + .alpha(1f) + .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION) + .setInterpolator(Interpolators.ALPHA_IN) + .start(); + } } private Rect getTaskRect(TaskView taskView) { @@ -733,8 +751,9 @@ public class RecentsView extends FrameLayout { viewState.dockAreaOverlay.setCallback(this); viewState.dockAreaOverlay.setBounds(bounds); } - viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION, - Interpolators.ALPHA_IN, animateAlpha, animateBounds); + viewState.startAnimation(bounds, areaAlpha, hintAlpha, + DOCK_AREA_OVERLAY_TRANSITION_DURATION, Interpolators.ALPHA_IN, + animateAlpha, animateBounds); } } }