Tweaking stack shadows and layout.

- Properly setting view outline alpha
- Ensuring that dismissing while in focused state will return to
  non-focused state
- Fixing mis-calculation with bottom stack area

Change-Id: I281b7707421ffde4225180c63c7d40bf325f7f72
This commit is contained in:
Winson
2016-02-15 15:40:08 -08:00
parent 680888123d
commit 1499150478
11 changed files with 102 additions and 40 deletions

View File

@@ -31,6 +31,8 @@
<!-- The radius of the rounded corners on a task view. -->
<dimen name="recents_task_view_rounded_corners_radius">3dp</dimen>
<!-- The radius of the rounded corners on a task view's shadow. -->
<dimen name="recents_task_view_shadow_rounded_corners_radius">18dp</dimen>
<!-- The fraction of the screen height where the clock on the Keyguard has its center. The
max value is used when no notifications are displaying, and the min value is when the

View File

@@ -227,12 +227,14 @@
<!-- The radius of the rounded corners on a task view. -->
<dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
<!-- The radius of the rounded corners on a task view's shadow. -->
<dimen name="recents_task_view_shadow_rounded_corners_radius">12dp</dimen>
<!-- The min translation in the Z index for the last task. -->
<dimen name="recents_task_view_z_min">16dp</dimen>
<dimen name="recents_task_view_z_min">3dp</dimen>
<!-- The max translation in the Z index for the last task. -->
<dimen name="recents_task_view_z_max">48dp</dimen>
<dimen name="recents_task_view_z_max">24dp</dimen>
<!-- The amount to translate when animating the removal of a task. -->
<dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
@@ -273,11 +275,11 @@
<!-- The amount to allow the stack to overscroll. -->
<dimen name="recents_stack_overscroll">24dp</dimen>
<!-- The size of the peek area at the top of the stack. -->
<!-- The size of the peek area at the top of the stack (below the status bar). -->
<dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
<!-- The size of the peek area at the bottom of the stack. -->
<dimen name="recents_layout_focused_bottom_peek_size">@dimen/recents_history_button_height</dimen>
<!-- The size of each task peek area at the bottom of the stack (above the nav bar). -->
<dimen name="recents_layout_focused_bottom_task_peek_size">16dp</dimen>
<!-- The height of the history button. -->
<dimen name="recents_history_button_height">48dp</dimen>

View File

@@ -81,6 +81,13 @@ public class AnimateableViewBounds extends ViewOutlineProvider {
}
}
/**
* @return the outline alpha.
*/
public float getAlpha() {
return mAlpha;
}
/** Sets the top clip. */
public void setClipTop(int top) {
mClipRect.top = top;

View File

@@ -153,6 +153,7 @@ public class FreeformWorkspaceLayoutAlgorithm {
transformOut.alpha = 1f;
transformOut.translationZ = stackLayout.mMaxTranslationZ;
transformOut.dimAlpha = 0f;
transformOut.viewOutlineAlpha = TaskStackLayoutAlgorithm.OUTLINE_ALPHA_MAX_VALUE;
transformOut.rect.set(ffRect);
transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
transformOut.visible = true;

View File

@@ -458,7 +458,8 @@ public class TaskStackAnimationHelper {
mStackView.cancelDeferredTaskViewLayoutAnimation();
// Get the final set of task transforms
mStackView.getLayoutTaskTransforms(newScroll, stackTasks, mTmpFinalTaskTransforms);
mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
mTmpFinalTaskTransforms);
// Focus the task view
TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);

View File

@@ -108,8 +108,13 @@ public class TaskStackLayoutAlgorithm {
// The scale factor to apply to the user movement in the stack to unfocus it
private static final float UNFOCUS_MULTIPLIER = 0.8f;
// The distribution of view bounds alpha
// XXX: This is a hack because you can currently set the max alpha to be > 1f
public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
// The distribution of dim to apply to tasks in the stack
public static final float DIM_MAX_VALUE = 0.35f;
private static final float DIM_MAX_VALUE = 0.35f;
private static final Path UNFOCUSED_DIM_PATH = new Path();
private static final Path FOCUSED_DIM_PATH = new Path();
static {
@@ -263,7 +268,7 @@ public class TaskStackLayoutAlgorithm {
@ViewDebug.ExportedProperty(category="recents")
private int mFocusedTopPeekHeight;
@ViewDebug.ExportedProperty(category="recents")
private int mFocusedBottomPeekHeight;
private int mFocusedBottomTaskPeekHeight;
// The offset from the top of the stack to the top of the bounds when the stack is scrolled to
// the end
@@ -337,8 +342,8 @@ public class TaskStackLayoutAlgorithm {
mFocusState = getDefaultFocusState();
mFocusedTopPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
mFocusedBottomPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_peek_size);
mFocusedBottomTaskPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_task_peek_size);
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
@@ -607,7 +612,7 @@ public class TaskStackLayoutAlgorithm {
boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
if (isFrontMostTaskInGroup) {
getStackTransform(taskProgress, mInitialScrollP, tmpTransform, null,
getStackTransform(taskProgress, mInitialScrollP, mFocusState, tmpTransform, null,
false /* ignoreSingleTaskCase */, false /* forceUpdate */);
float screenY = tmpTransform.rect.top;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
@@ -641,11 +646,11 @@ public class TaskStackLayoutAlgorithm {
*/
public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
return getStackTransform(task, stackScroll, transformOut, frontTransform,
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
false /* forceUpdate */);
}
public TaskViewTransform getStackTransform(Task task, float stackScroll,
public TaskViewTransform getStackTransform(Task task, float stackScroll, float focusState,
TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) {
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
@@ -656,7 +661,7 @@ public class TaskStackLayoutAlgorithm {
transformOut.reset();
return transformOut;
}
getStackTransform(mTaskIndexMap.get(task.key), stackScroll, transformOut,
getStackTransform(mTaskIndexMap.get(task.key), stackScroll, focusState, transformOut,
frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
return transformOut;
}
@@ -682,7 +687,7 @@ public class TaskStackLayoutAlgorithm {
* internally to ensure that we can calculate the transform for any
* position in the stack.
*/
public void getStackTransform(float taskProgress, float stackScroll,
public void getStackTransform(float taskProgress, float stackScroll, float focusState,
TaskViewTransform transformOut, TaskViewTransform frontTransform,
boolean ignoreSingleTaskCase, boolean forceUpdate) {
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -706,6 +711,7 @@ public class TaskStackLayoutAlgorithm {
int y;
float z;
float dimAlpha;
float viewOutlineAlpha;
if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1 && !ignoreSingleTaskCase) {
// When there is exactly one task, then decouple the task from the stack and just move
// in screen space
@@ -715,6 +721,7 @@ public class TaskStackLayoutAlgorithm {
y = centerYOffset + getYForDeltaP(tmpP, 0);
z = mMaxTranslationZ;
dimAlpha = 0f;
viewOutlineAlpha = (OUTLINE_ALPHA_MIN_VALUE + OUTLINE_ALPHA_MAX_VALUE) / 2f;
} else {
// Otherwise, update the task to the stack layout
@@ -726,16 +733,20 @@ public class TaskStackLayoutAlgorithm {
float focusedDim = 1f - FOCUSED_DIM_INTERPOLATOR.getInterpolation(focusedRangeX);
y = (mStackRect.top - mTaskRect.top) +
(int) Utilities.mapRange(mFocusState, unfocusedY, focusedY);
z = Utilities.clamp01(unfocusedRangeX) * mMaxTranslationZ;
dimAlpha = Utilities.mapRange(mFocusState, unfocusedDim, focusedDim);
(int) Utilities.mapRange(focusState, unfocusedY, focusedY);
z = Utilities.mapRange(Utilities.clamp01(unfocusedRangeX), mMinTranslationZ,
mMaxTranslationZ);
dimAlpha = DIM_MAX_VALUE * Utilities.mapRange(focusState, unfocusedDim, focusedDim);
viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(unfocusedRangeX),
OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE);
}
// Fill out the transform
transformOut.scale = 1f;
transformOut.alpha = 1f;
transformOut.translationZ = z;
transformOut.dimAlpha = DIM_MAX_VALUE * dimAlpha;
transformOut.dimAlpha = dimAlpha;
transformOut.viewOutlineAlpha = viewOutlineAlpha;
transformOut.rect.set(mTaskRect);
transformOut.rect.offset(x, y);
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
@@ -788,8 +799,9 @@ public class TaskStackLayoutAlgorithm {
// linear pieces that goes from (0,1) through (0.5, peek height offset),
// (0.5, bottom task offsets), and (1,0).
float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
float bottomPeekHeightPct = (float) Math.max(mFocusedBottomPeekHeight, mStackRect.bottom -
mTaskRect.bottom) / mStackRect.height();
float bottomPeekHeightPct = Math.max(
mSystemInsets.bottom + mFocusedRange.relativeMax * mFocusedBottomTaskPeekHeight,
mStackBottomOffset + mFocusedBottomTaskPeekHeight) / mStackRect.height();
Path p = new Path();
p.moveTo(0f, 1f);
p.lineTo(0.5f, 1f - topPeekHeightPct);
@@ -835,10 +847,10 @@ public class TaskStackLayoutAlgorithm {
mFocusedRange.relativeMin);
float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
mFocusedRange.relativeMax);
getStackTransform(min, 0f, mBackOfStackTransform, null, true /* ignoreSingleTaskCase */,
true /* forceUpdate */);
getStackTransform(max, 0f, mFrontOfStackTransform, null, true /* ignoreSingleTaskCase */,
true /* forceUpdate */);
getStackTransform(min, 0f, mFocusState, mBackOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
getStackTransform(max, 0f, mFocusState, mFrontOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
mBackOfStackTransform.visible = true;
mFrontOfStackTransform.visible = true;
}

View File

@@ -665,6 +665,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void getCurrentTaskTransforms(ArrayList<Task> tasks,
ArrayList<TaskViewTransform> transformsOut) {
Utilities.matchTaskListSize(tasks, transformsOut);
float focusState = mLayoutAlgorithm.getFocusState();
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
@@ -673,7 +674,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
transform.fillIn(tv);
} else {
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
transform, null, true /* forceUpdate */);
focusState, transform, null, true /* forceUpdate */);
}
transform.visible = true;
}
@@ -681,15 +682,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**
* Returns the task transforms for all the tasks in the stack if the stack was at the given
* {@param stackScroll}.
* {@param stackScroll} and {@param focusState}.
*/
public void getLayoutTaskTransforms(float stackScroll, ArrayList<Task> tasks,
public void getLayoutTaskTransforms(float stackScroll, float focusState, ArrayList<Task> tasks,
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, transform, null,
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
true /* forceUpdate */);
transform.visible = true;
}
@@ -1510,7 +1511,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
public void onScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
mUIDozeTrigger.poke();
if (animation != null) {
relayoutTaskViewsOnNextFrame(animation);

View File

@@ -39,7 +39,7 @@ public class TaskStackViewScroller {
private static final boolean DEBUG = false;
public interface TaskStackViewScrollerCallbacks {
void onScrollChanged(float prevScroll, float curScroll, AnimationProps animation);
void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation);
}
/**
@@ -106,7 +106,7 @@ public class TaskStackViewScroller {
float prevStackScroll = mStackScrollP;
mStackScrollP = s;
if (mCb != null) {
mCb.onScrollChanged(prevStackScroll, mStackScrollP, animation);
mCb.onStackScrollChanged(prevStackScroll, mStackScrollP, animation);
}
}

View File

@@ -401,6 +401,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mCurrentTasks = mSv.getStack().getStackTasks();
MutableBoolean isFrontMostTask = new MutableBoolean(false);
Task anchorTask = mSv.findAnchorTask(mCurrentTasks, isFrontMostTask);
TaskStackLayoutAlgorithm layoutAlgorithm = mSv.getStackAlgorithm();
TaskStackViewScroller stackScroller = mSv.getScroller();
if (anchorTask != null) {
// Get the current set of task transforms
@@ -411,7 +412,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
float prevAnchorTaskScroll = 0;
boolean pullStackForward = mCurrentTasks.size() > 0;
if (pullStackForward) {
prevAnchorTaskScroll = mSv.getStackAlgorithm().getStackScrollForTask(anchorTask);
prevAnchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
}
// Calculate where the views would be without the deleting tasks
@@ -423,9 +424,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
newStackScroll = stackScroller.getBoundedStackScroll(newStackScroll);
} else if (pullStackForward) {
// Otherwise, offset the scroll by the movement of the anchor task
float anchorTaskScroll = mSv.getStackAlgorithm().getStackScrollForTask(anchorTask);
float anchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
if (mSv.getStackAlgorithm().getFocusState() !=
if (layoutAlgorithm.getFocusState() !=
TaskStackLayoutAlgorithm.STATE_FOCUSED) {
// If we are focused, we don't want the front task to move, but otherwise, we
// allow the back task to move up, and the front task to move back
@@ -439,7 +440,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mSv.bindVisibleTaskViews(newStackScroll);
// Get the final set of task transforms (with task removed)
mSv.getLayoutTaskTransforms(newStackScroll, mCurrentTasks, mFinalTaskTransforms);
mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
mCurrentTasks, mFinalTaskTransforms);
// Set the target to scroll towards upon dismissal
mTargetStackScroll = newStackScroll;
@@ -448,7 +450,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
* Post condition: All views that will be visible as a part of the gesture are retrieved
* and at their initial positions. The stack is still at the current
* scroll, but the layout is updated without the task currently being
* dismissed.
* dismissed. The final layout is in the unfocused stack state, which
* will be applied when the current task is dismissed.
*/
}
}
@@ -472,6 +475,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
tv.setTouchEnabled(true);
// Update the scroll to the final scroll position from onBeginDrag()
mSv.getScroller().setStackScroll(mTargetStackScroll, null);
// Update the focus state to the final focus state
mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
// Remove the task view from the stack
EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
// Stop tracking this deletion animation
@@ -547,6 +552,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
fromTransform.rect, toTransform.rect));
mTmpTransform.dimAlpha = fromTransform.dimAlpha + (toTransform.dimAlpha -
fromTransform.dimAlpha) * dismissFraction;
mTmpTransform.viewOutlineAlpha = fromTransform.viewOutlineAlpha +
(toTransform.viewOutlineAlpha - fromTransform.viewOutlineAlpha) *
dismissFraction;
mTmpTransform.translationZ = fromTransform.translationZ +
(toTransform.translationZ - fromTransform.translationZ) * dismissFraction;

View File

@@ -80,7 +80,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
* launching) needs to be animated independently of the task progress.
*/
public static final Property<TaskView, Float> DIM_ALPHA =
new FloatProperty<TaskView>("dim") {
new FloatProperty<TaskView>("dimAlpha") {
@Override
public void setValue(TaskView tv, float dimAlpha) {
tv.setDimAlpha(dimAlpha);
@@ -92,6 +92,23 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
}
};
/**
* The dim overlay is generally calculated from the task progress, but occasionally (like when
* launching) needs to be animated independently of the task progress.
*/
public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
new FloatProperty<TaskView>("viewOutlineAlpha") {
@Override
public void setValue(TaskView tv, float alpha) {
tv.getViewBounds().setAlpha(alpha);
}
@Override
public Float get(TaskView tv) {
return tv.getViewBounds().getAlpha();
}
};
@ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
@@ -144,7 +161,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
RecentsConfiguration config = Recents.getConfiguration();
Resources res = context.getResources();
mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius));
R.dimen.recents_task_view_shadow_rounded_corners_radius));
if (config.fakeShadows) {
setBackground(new FakeShadowDrawable(res, config));
}
@@ -251,6 +268,9 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
setDimAlpha(toTransform.dimAlpha);
}
if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
}
// Manually call back to the animator listener and update callback
if (toAnimation.getListener() != null) {
toAnimation.getListener().onAnimationEnd(null);
@@ -265,6 +285,11 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
toTransform.dimAlpha);
mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
}
if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
ObjectAnimator anim = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
}
if (updateCallback != null) {
ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
updateCallbackAnim.addUpdateListener(updateCallback);
@@ -367,7 +392,6 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
int dimAlphaInt = (int) (dimAlpha * 255);
mDimAlpha = dimAlpha;
mViewBounds.setAlpha(1f - (dimAlpha / TaskStackLayoutAlgorithm.DIM_MAX_VALUE));
if (config.useHardwareLayers) {
// Defer setting hardware layers if we have not yet measured, or there is no dim to draw
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {

View File

@@ -87,6 +87,7 @@ public class TaskViewTransform {
public float scale = 1f;
public float alpha = 1f;
public float dimAlpha = 0f;
public float viewOutlineAlpha = 0f;
public boolean visible = false;
@@ -102,6 +103,7 @@ public class TaskViewTransform {
alpha = tv.getAlpha();
visible = true;
dimAlpha = tv.getDimAlpha();
viewOutlineAlpha = tv.getViewBounds().getAlpha();
rect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
}
@@ -114,6 +116,7 @@ public class TaskViewTransform {
alpha = other.alpha;
visible = other.visible;
dimAlpha = other.dimAlpha;
viewOutlineAlpha = other.viewOutlineAlpha;
rect.set(other.rect);
}
@@ -125,6 +128,7 @@ public class TaskViewTransform {
scale = 1f;
alpha = 1f;
dimAlpha = 0f;
viewOutlineAlpha = 0f;
visible = false;
rect.setEmpty();
}