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/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/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/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/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/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/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index d55c7d80f0ee5..6ecd52dbccc50 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 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 newDockStatesSet = Utilities.arrayToSet(newDockStates, new ArraySet()); ArrayList 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 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); } } 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..bdc4c1a12117f 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 a75d1e1339e29..6176d99a7c174 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) { @@ -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 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..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); } @@ -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 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; 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 }