diff options
| author | 2016-04-19 01:32:36 +0000 | |
|---|---|---|
| committer | 2016-04-19 01:32:37 +0000 | |
| commit | 87574b7b1a26ca1c26a26016ff1cee286404d84c (patch) | |
| tree | 15bebcfe9887fad0c051c25885c104c0192f5246 | |
| parent | 84f0b791e2eb0170c6b797929235c98d44f12d47 (diff) | |
| parent | b677bcbee460a3246872d8090b64db1d8ec9c276 (diff) | |
Merge changes Ibec23fdb,Id7be5129,I25af8fad,Idbabfe39,I86b10805, ... into nyc-dev
* changes:
Hiding clear-all button on drag start.
Adding hint text for drop targets.
Fixing animation spec animation problem.
Making the dismiss animation feel smoother.
Fixing issue with gap between task views.
Adding desired undocking animation.
21 files changed, 428 insertions, 204 deletions
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml index 625e9c111672..43b3de1a0244 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/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 03b9837e6175..504361008570 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -640,6 +640,9 @@ <!-- The amount of overscroll allowed when flinging to the end of the stack. --> <dimen name="recents_fling_overscroll_distance">24dp</dimen> + <!-- The size of the drag hint text. --> + <dimen name="recents_drag_hint_text_size">14sp</dimen> + <!-- The min alpha to apply to a task affiliation group color. --> <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 37b00bbfdc27..a03aa28c47c5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -737,6 +737,8 @@ <string name="recents_stack_action_button_label">Clear all</string> <!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] --> <string name="recents_incompatible_app_message">App doesn\'t support split screen</string> + <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] --> + <string name="recents_drag_hint_message">Drag here to use split screen</string> <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] --> <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string> diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index e83819179c24..9a6fa9c6bae6 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/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 4d69280dfa22..a58e12e169dc 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 0413bc9fcc4e..e192da7bb547 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 cf2a68e2cf95..11649fbbb6a6 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 82c81ae3e5d3..fb92971703d5 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/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 95e276f7c8cf..47995c4b73f3 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<ViewState> HINT_ALPHA = + new IntProperty<ViewState>("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; - - private ViewState(int alpha) { - dockAreaAlpha = alpha; + public final int hintTextAlpha; + public final int hintTextOrientation; + + 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<Animator> 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,17 +421,26 @@ 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/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index db5413f85c24..04f10ef8e893 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/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index d55c7d80f0ee..6ecd52dbccc5 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; @@ -91,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; @@ -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) { @@ -424,10 +426,7 @@ public class RecentsView extends FrameLayout { ArrayList<TaskStack.DockState> 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); } } @@ -463,18 +462,29 @@ 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 */); + + // 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) { 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) { @@ -496,13 +506,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(); @@ -546,9 +552,18 @@ 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 */); } + + // 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) { @@ -622,6 +637,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. */ @@ -704,8 +723,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<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates, new ArraySet<TaskStack.DockState>()); ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); @@ -714,11 +733,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(), @@ -727,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); } } } 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 70c4dbdc9665..214ec90fe5ee 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); } } 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 dce235318733..06a2c1e4af78 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 665d9ad94002..e79306f55e3d 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<Task> 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<TaskView> 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 34d6bcecdee6..bdc4c1a12117 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 @@ -577,7 +574,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 +582,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, @@ -834,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; } /** @@ -938,7 +942,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 a75d1e1339e2..6176d99a7c17 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<Task.TaskKey> ignoreTasksSet, - boolean ignoreTaskOverrides) { + void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) { // Get all the task transforms ArrayList<Task> 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<Task.TaskKey>, 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<Task.TaskKey> 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<Task> tasks, - ArrayList<TaskViewTransform> transformsOut) { + boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> 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<Task.TaskKey>) */ 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<Task.TaskKey> 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) { @@ -1640,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); @@ -1834,7 +1811,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 +1925,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<TaskView> 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 3cdb1fb27854..9edf9d6c66c4 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); } @@ -483,7 +492,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; @@ -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/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 37f5a9fc9974..7ea70b587fd7 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 fb0fc3076f30..aac6d13706a7 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 4de7713eb2fb..8977f504209f 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; 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 8b52bf65a673..4986740ebc3f 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 } |