Updating the stack layout to use a parameterized curve.
- Fixing issue with search box not being layered on top of the task stack view (Bug 16643875) - Fixing issue with there being no animation when dismissing recents while the stack is scrolling. Change-Id: I990f3c527de655d62fbf8a4539dcbaed3ed422c8
This commit is contained in:
committed by
Winson Chung
parent
7c3a95633d
commit
012ef36a6c
@ -201,8 +201,8 @@
|
||||
<!-- The min translation in the Z index for the last task. -->
|
||||
<dimen name="recents_task_view_z_min">5dp</dimen>
|
||||
|
||||
<!-- The translation in the Z index for each task above the last task. -->
|
||||
<dimen name="recents_task_view_z_increment">10dp</dimen>
|
||||
<!-- The max translation in the Z index for the last task. -->
|
||||
<dimen name="recents_task_view_z_max">65dp</dimen>
|
||||
|
||||
<!-- The amount to translate when animating the removal of a task. -->
|
||||
<dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
|
||||
|
@ -105,7 +105,8 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
// Do nothing
|
||||
// Initialize some static datastructures
|
||||
TaskStackViewLayoutAlgorithm.initializeCurve();
|
||||
}
|
||||
|
||||
public void onBootCompleted() {
|
||||
@ -322,7 +323,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
Rect taskStackBounds = new Rect(mTaskStackBounds);
|
||||
taskStackBounds.bottom -= mSystemInsets.bottom;
|
||||
tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
|
||||
tsv.setStackScrollToInitialState();
|
||||
tsv.getScroller().setStackScrollToInitialState();
|
||||
|
||||
// Find the running task in the TaskStack
|
||||
Task task = null;
|
||||
@ -344,7 +345,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
|
||||
}
|
||||
|
||||
// Get the transform for the running task
|
||||
mTmpTransform = algo.getStackTransform(task, tsv.getStackScroll(), mTmpTransform);
|
||||
mTmpTransform = algo.getStackTransform(task, tsv.getScroller().getStackScroll(), mTmpTransform, null);
|
||||
return new Rect(mTmpTransform.rect);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,6 @@ public class Constants {
|
||||
public static final boolean Verbose = false;
|
||||
|
||||
public static class App {
|
||||
// Enables the simulated task affiliations
|
||||
public static final boolean EnableSimulatedTaskGroups = false;
|
||||
// Enables the screenshot app->Recents transition
|
||||
public static final boolean EnableScreenshotAppTransition = false;
|
||||
// Enables the filtering of tasks according to their grouping
|
||||
@ -43,11 +41,15 @@ public class Constants {
|
||||
public static final boolean EnableShadows = true;
|
||||
// This disables the bitmap and icon caches
|
||||
public static final boolean DisableBackgroundCache = false;
|
||||
// For debugging, this enables us to create mock recents tasks
|
||||
// Enables the simulated task affiliations
|
||||
public static final boolean EnableSimulatedTaskGroups = false;
|
||||
// Defines the number of mock task affiliations per group
|
||||
public static final int TaskAffiliationsGroupCount = 12;
|
||||
// Enables us to create mock recents tasks
|
||||
public static final boolean EnableSystemServicesProxy = false;
|
||||
// For debugging, this defines the number of mock recents packages to create
|
||||
// Defines the number of mock recents packages to create
|
||||
public static final int SystemServicesProxyMockPackageCount = 3;
|
||||
// For debugging, this defines the number of mock recents tasks to create
|
||||
// Defines the number of mock recents tasks to create
|
||||
public static final int SystemServicesProxyMockTaskCount = 100;
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public class RecentsConfiguration {
|
||||
public int taskViewRemoveAnimDuration;
|
||||
public int taskViewRemoveAnimTranslationXPx;
|
||||
public int taskViewTranslationZMinPx;
|
||||
public int taskViewTranslationZIncrementPx;
|
||||
public int taskViewTranslationZMaxPx;
|
||||
public int taskViewRoundedCornerRadiusPx;
|
||||
public int taskViewHighlightPx;
|
||||
public int taskViewAffiliateGroupEnterOffsetPx;
|
||||
@ -208,8 +208,7 @@ public class RecentsConfiguration {
|
||||
res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
|
||||
taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
|
||||
taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
|
||||
taskViewTranslationZIncrementPx =
|
||||
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
|
||||
taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
|
||||
taskViewAffiliateGroupEnterOffsetPx =
|
||||
res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset);
|
||||
|
||||
|
@ -337,7 +337,7 @@ public class TaskStack {
|
||||
String prevPackage = "";
|
||||
int prevAffiliation = -1;
|
||||
Random r = new Random();
|
||||
int groupCountDown = 5;
|
||||
int groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
Task t = tasks.get(i);
|
||||
String packageName = t.key.baseIntent.getComponent().getPackageName();
|
||||
@ -352,7 +352,7 @@ public class TaskStack {
|
||||
addGroup(group);
|
||||
prevAffiliation = affiliation;
|
||||
prevPackage = packageName;
|
||||
groupCountDown = 5;
|
||||
groupCountDown = Constants.DebugFlags.App.TaskAffiliationsGroupCount;
|
||||
}
|
||||
group.addTask(t);
|
||||
taskMap.put(t.key, t);
|
||||
|
@ -26,6 +26,7 @@ import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.SeekBar;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -42,11 +43,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
|
||||
|
||||
final static int sCornerRectSize = 50;
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
DebugOverlayViewCallbacks mCb;
|
||||
|
||||
ArrayList<Pair<Rect, Integer>> mRects = new ArrayList<Pair<Rect, Integer>>();
|
||||
String mText;
|
||||
Paint mDebugOutline = new Paint();
|
||||
Paint mTmpPaint = new Paint();
|
||||
Rect mTmpRect = new Rect();
|
||||
boolean mEnabled = true;
|
||||
|
||||
SeekBar mPrimarySeekBar;
|
||||
@ -66,6 +70,7 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
|
||||
|
||||
public DebugOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mDebugOutline.setColor(0xFFff0000);
|
||||
mDebugOutline.setStyle(Paint.Style.STROKE);
|
||||
mDebugOutline.setStrokeWidth(8f);
|
||||
@ -124,6 +129,12 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Sets the debug text at the bottom of the screen. */
|
||||
void setText(String message) {
|
||||
mText = message;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
@ -145,6 +156,14 @@ public class DebugOverlayView extends FrameLayout implements SeekBar.OnSeekBarCh
|
||||
mTmpPaint.setColor(r.second);
|
||||
canvas.drawRect(r.first, mTmpPaint);
|
||||
}
|
||||
|
||||
// Draw the text
|
||||
if (mText != null && mText.length() > 0) {
|
||||
mTmpPaint.setColor(0xFFff0000);
|
||||
mTmpPaint.setTextSize(60);
|
||||
mTmpPaint.getTextBounds(mText, 0, 1, mTmpRect);
|
||||
canvas.drawText(mText, 10f, getMeasuredHeight() - mTmpRect.height() - mConfig.systemInsets.bottom, mTmpPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,6 +225,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
public void setSearchBarVisibility(int visibility) {
|
||||
if (mSearchBar != null) {
|
||||
mSearchBar.setVisibility(visibility);
|
||||
// Always bring the search bar to the top
|
||||
mSearchBar.bringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,17 +366,17 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
|
||||
View sourceView = tv;
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
int stackScroll = stackView.getStackScroll();
|
||||
float stackScroll = stackView.getScroller().getStackScroll();
|
||||
if (tv == null) {
|
||||
// If there is no actual task view, then use the stack view as the source view
|
||||
// and then offset to the expected transform rect, but bound this to just
|
||||
// outside the display rect (to ensure we don't animate from too far away)
|
||||
sourceView = stackView;
|
||||
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
|
||||
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
|
||||
offsetX = transform.rect.left;
|
||||
offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
|
||||
} else {
|
||||
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform);
|
||||
transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
|
||||
}
|
||||
|
||||
// Compute the thumbnail to scale up from
|
||||
|
@ -126,28 +126,6 @@ class TaskBarView extends FrameLayout {
|
||||
mIsFullscreen = isFullscreen;
|
||||
}
|
||||
|
||||
/** Synchronizes this bar view's properties with the task's transform */
|
||||
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
|
||||
if (duration > 0 && (mDismissButton.getVisibility() == View.VISIBLE)) {
|
||||
ViewPropertyAnimator anim = mDismissButton.animate();
|
||||
|
||||
// Animate to the final state
|
||||
if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
|
||||
anim.alpha(toTransform.dismissAlpha)
|
||||
.setStartDelay(0)
|
||||
.setDuration(duration)
|
||||
.setInterpolator(mConfig.fastOutSlowInInterpolator)
|
||||
.withLayer()
|
||||
.start();
|
||||
}
|
||||
} else {
|
||||
// Set the changed properties
|
||||
if (toTransform.hasDismissAlphaChangedFrom(mDismissButton.getAlpha())) {
|
||||
mDismissButton.setAlpha(toTransform.dismissAlpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
|
@ -16,25 +16,17 @@
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.OverScroller;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.recents.Constants;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.misc.DozeTrigger;
|
||||
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.RecentsPackageMonitor;
|
||||
import com.android.systemui.recents.model.RecentsTaskLoader;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
@ -47,8 +39,8 @@ import java.util.HashSet;
|
||||
|
||||
/* The visual representation of a task stack view */
|
||||
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
|
||||
TaskView.TaskViewCallbacks, ViewPool.ViewPoolConsumer<TaskView, Task>,
|
||||
RecentsPackageMonitor.PackageCallbacks {
|
||||
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
|
||||
ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
|
||||
|
||||
/** The TaskView callbacks */
|
||||
interface TaskStackViewCallbacks {
|
||||
@ -64,8 +56,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
TaskStack mStack;
|
||||
TaskStackViewLayoutAlgorithm mStackAlgorithm;
|
||||
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
|
||||
TaskStackViewFilterAlgorithm mFilterAlgorithm;
|
||||
TaskStackViewScroller mStackScroller;
|
||||
TaskStackViewTouchHandler mTouchHandler;
|
||||
TaskStackViewCallbacks mCb;
|
||||
ViewPool<TaskView, Task> mViewPool;
|
||||
@ -73,18 +66,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
DozeTrigger mUIDozeTrigger;
|
||||
DebugOverlayView mDebugOverlay;
|
||||
Rect mTaskStackBounds = new Rect();
|
||||
|
||||
// The virtual stack scroll that we use for the card layout
|
||||
int mStackScroll;
|
||||
int mMinScroll;
|
||||
int mMaxScroll;
|
||||
int mStashedScroll;
|
||||
int mFocusedTaskIndex = -1;
|
||||
OverScroller mScroller;
|
||||
ObjectAnimator mScrollAnimator;
|
||||
|
||||
// Optimizations
|
||||
ReferenceCountedTrigger mHwLayersTrigger;
|
||||
int mStackViewsAnimationDuration;
|
||||
boolean mStackViewsDirty = true;
|
||||
boolean mAwaitingFirstLayout = true;
|
||||
@ -114,12 +98,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mStack = stack;
|
||||
mStack.setCallbacks(this);
|
||||
mScroller = new OverScroller(context);
|
||||
mTouchHandler = new TaskStackViewTouchHandler(context, this);
|
||||
mViewPool = new ViewPool<TaskView, Task>(context, this);
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mStackAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
|
||||
mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
|
||||
mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool);
|
||||
mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm);
|
||||
mStackScroller.setCallbacks(this);
|
||||
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
|
||||
mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -149,7 +134,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
void requestSynchronizeStackViewsWithModel(int duration) {
|
||||
if (!mStackViewsDirty) {
|
||||
invalidate(mStackAlgorithm.mStackRect);
|
||||
invalidate();
|
||||
mStackViewsDirty = true;
|
||||
}
|
||||
if (mAwaitingFirstLayout) {
|
||||
@ -174,7 +159,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
/** Returns the stack algorithm for this task stack. */
|
||||
public TaskStackViewLayoutAlgorithm getStackAlgorithm() {
|
||||
return mStackAlgorithm;
|
||||
return mLayoutAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,7 +167,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
*/
|
||||
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
|
||||
ArrayList<Task> tasks,
|
||||
int stackScroll,
|
||||
float stackScroll,
|
||||
int[] visibleRangeOut,
|
||||
boolean boundTranslationsToRect) {
|
||||
// XXX: We should be intelligent about where to look for the visible stack range using the
|
||||
@ -207,8 +192,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
}
|
||||
|
||||
// Update the stack transforms
|
||||
TaskViewTransform prevTransform = null;
|
||||
for (int i = taskCount - 1; i >= 0; i--) {
|
||||
TaskViewTransform transform = mStackAlgorithm.getStackTransform(tasks.get(i), stackScroll, taskTransforms.get(i));
|
||||
TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(tasks.get(i),
|
||||
stackScroll, taskTransforms.get(i), prevTransform);
|
||||
if (transform.visible) {
|
||||
if (frontMostVisibleIndex < 0) {
|
||||
frontMostVisibleIndex = i;
|
||||
@ -228,8 +215,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
if (boundTranslationsToRect) {
|
||||
transform.translationY = Math.min(transform.translationY,
|
||||
mStackAlgorithm.mViewRect.bottom);
|
||||
mLayoutAlgorithm.mViewRect.bottom);
|
||||
}
|
||||
prevTransform = transform;
|
||||
}
|
||||
if (visibleRangeOut != null) {
|
||||
visibleRangeOut[0] = frontMostVisibleIndex;
|
||||
@ -243,7 +231,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
* call is less optimal than calling updateStackTransforms directly.
|
||||
*/
|
||||
private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
|
||||
int stackScroll,
|
||||
float stackScroll,
|
||||
int[] visibleRangeOut,
|
||||
boolean boundTranslationsToRect) {
|
||||
ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
|
||||
@ -257,9 +245,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
if (mStackViewsDirty) {
|
||||
// Get all the task transforms
|
||||
ArrayList<Task> tasks = mStack.getTasks();
|
||||
int stackScroll = getStackScroll();
|
||||
float stackScroll = mStackScroller.getStackScroll();
|
||||
int[] visibleRange = mTmpVisibleRange;
|
||||
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
|
||||
boolean isValidVisibleRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
|
||||
stackScroll, visibleRange, false);
|
||||
if (mDebugOverlay != null) {
|
||||
mDebugOverlay.setText("vis[" + visibleRange[1] + "-" + visibleRange[0] + "]");
|
||||
}
|
||||
|
||||
// Return all the invisible children to the pool
|
||||
mTmpTaskViewMap.clear();
|
||||
@ -288,11 +280,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
if (mStackViewsAnimationDuration > 0) {
|
||||
// For items in the list, put them in start animating them from the
|
||||
// approriate ends of the list where they are expected to appear
|
||||
if (transform.t < 0) {
|
||||
mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
|
||||
if (Float.compare(transform.p, 0f) <= 0) {
|
||||
mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpTransform, null);
|
||||
} else {
|
||||
int nextTaskStackScroll = mStackAlgorithm.getStackScrollForTaskIndex(task, 1);
|
||||
mStackAlgorithm.getStackTransform(nextTaskStackScroll, stackScroll, mTmpTransform);
|
||||
mLayoutAlgorithm.getStackTransform(1f, 0f, mTmpTransform, null);
|
||||
}
|
||||
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
|
||||
}
|
||||
@ -357,143 +348,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
mTaskStackBounds.set(r);
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll */
|
||||
public void setStackScroll(int value) {
|
||||
mStackScroll = value;
|
||||
mUIDozeTrigger.poke();
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll without synchronizing the stack view with the model */
|
||||
public void setStackScrollRaw(int value) {
|
||||
mStackScroll = value;
|
||||
mUIDozeTrigger.poke();
|
||||
}
|
||||
/** Sets the current stack scroll to the initial state when you first enter recents */
|
||||
public void setStackScrollToInitialState() {
|
||||
setStackScroll(getInitialStackScroll());
|
||||
}
|
||||
/** Computes the initial stack scroll for the stack. */
|
||||
int getInitialStackScroll() {
|
||||
if (mStack.getTaskCount() > 2) {
|
||||
return Math.max(mMinScroll, mMaxScroll - (int) (mStackAlgorithm.mTaskRect.height() * (3f/4f)));
|
||||
}
|
||||
return mMaxScroll;
|
||||
}
|
||||
|
||||
/** Gets the current stack scroll */
|
||||
public int getStackScroll() {
|
||||
return mStackScroll;
|
||||
}
|
||||
|
||||
/** Animates the stack scroll into bounds */
|
||||
ObjectAnimator animateBoundScroll() {
|
||||
int curScroll = getStackScroll();
|
||||
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
|
||||
if (newScroll != curScroll) {
|
||||
// Start a new scroll animation
|
||||
animateScroll(curScroll, newScroll, null);
|
||||
}
|
||||
return mScrollAnimator;
|
||||
}
|
||||
|
||||
/** Animates the stack scroll */
|
||||
void animateScroll(int curScroll, int newScroll, final Runnable postRunnable) {
|
||||
// Abort any current animations
|
||||
abortScroller();
|
||||
abortBoundScrollAnimation();
|
||||
|
||||
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
|
||||
mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
|
||||
curScroll, 250));
|
||||
mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setStackScroll((Integer) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (postRunnable != null) {
|
||||
postRunnable.run();
|
||||
}
|
||||
mScrollAnimator.removeAllListeners();
|
||||
}
|
||||
});
|
||||
mScrollAnimator.start();
|
||||
}
|
||||
|
||||
/** Aborts any current stack scrolls */
|
||||
void abortBoundScrollAnimation() {
|
||||
if (mScrollAnimator != null) {
|
||||
mScrollAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/** Aborts the scroller and any current fling */
|
||||
void abortScroller() {
|
||||
if (!mScroller.isFinished()) {
|
||||
// Abort the scroller
|
||||
mScroller.abortAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/** Bounds the current scroll if necessary */
|
||||
public boolean boundScroll() {
|
||||
int curScroll = getStackScroll();
|
||||
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
|
||||
if (newScroll != curScroll) {
|
||||
setStackScroll(newScroll);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bounds the current scroll if necessary, but does not synchronize the stack view with the
|
||||
* model.
|
||||
*/
|
||||
public boolean boundScrollRaw() {
|
||||
int curScroll = getStackScroll();
|
||||
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
|
||||
if (newScroll != curScroll) {
|
||||
setStackScrollRaw(newScroll);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Returns the amount that the scroll is out of bounds */
|
||||
int getScrollAmountOutOfBounds(int scroll) {
|
||||
if (scroll < mMinScroll) {
|
||||
return mMinScroll - scroll;
|
||||
} else if (scroll > mMaxScroll) {
|
||||
return scroll - mMaxScroll;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns whether the specified scroll is out of bounds */
|
||||
boolean isScrollOutOfBounds() {
|
||||
return getScrollAmountOutOfBounds(mStackScroll) != 0;
|
||||
}
|
||||
|
||||
/** Updates the min and max virtual scroll bounds */
|
||||
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
|
||||
// Compute the min and max scroll values
|
||||
mStackAlgorithm.computeMinMaxScroll(mStack.getTasks());
|
||||
mMinScroll = mStackAlgorithm.mMinScroll;
|
||||
mMaxScroll = mStackAlgorithm.mMaxScroll;
|
||||
mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
|
||||
|
||||
// Debug logging
|
||||
if (boundScrollToNewMinMax) {
|
||||
boundScroll();
|
||||
mStackScroller.boundScroll();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the scroller. */
|
||||
public TaskStackViewScroller getScroller() {
|
||||
return mStackScroller;
|
||||
}
|
||||
|
||||
/** Focuses the task at the specified index in the stack */
|
||||
void focusTask(int taskIndex, boolean scrollToNewPosition) {
|
||||
if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
|
||||
@ -520,10 +390,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
if (scrollToNewPosition) {
|
||||
// Scroll the view into position
|
||||
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll,
|
||||
mStackAlgorithm.getStackScrollForTaskIndex(t)));
|
||||
|
||||
animateScroll(getStackScroll(), newScroll, postScrollRunnable);
|
||||
// XXX: We probably want this to be centered in view instead of p = 0f
|
||||
float newScroll = mStackScroller.getBoundedStackScroll(
|
||||
mLayoutAlgorithm.getStackScrollForTaskIndex(t));
|
||||
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
|
||||
} else {
|
||||
if (postScrollRunnable != null) {
|
||||
postScrollRunnable.run();
|
||||
@ -558,24 +428,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
setStackScroll(mScroller.getCurrY());
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
// Synchronize the views
|
||||
if (synchronizeStackViewsWithModel()) {
|
||||
clipTaskViews();
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
mStackScroller.computeScroll();
|
||||
}
|
||||
|
||||
/** Computes the stack and task rects */
|
||||
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
|
||||
// Compute the rects in the stack algorithm
|
||||
mStackAlgorithm.computeRects(mStack.getTasks(), windowWidth, windowHeight, taskStackBounds);
|
||||
mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
|
||||
|
||||
// Update the scroll bounds
|
||||
updateMinMaxScroll(false);
|
||||
@ -598,7 +461,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
// If this is the first layout, then scroll to the front of the stack and synchronize the
|
||||
// stack views immediately to load all the views
|
||||
if (mAwaitingFirstLayout) {
|
||||
setStackScrollToInitialState();
|
||||
mStackScroller.setStackScrollToInitialState();
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
synchronizeStackViewsWithModel();
|
||||
}
|
||||
@ -611,9 +474,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
tv.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
} else {
|
||||
tv.measure(
|
||||
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(),
|
||||
MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.width(),
|
||||
MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
|
||||
MeasureSpec.makeMeasureSpec(mLayoutAlgorithm.mTaskRect.height() +
|
||||
tv.getMaxFooterHeight(), MeasureSpec.EXACTLY));
|
||||
}
|
||||
}
|
||||
@ -635,8 +498,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
if (tv.isFullScreenView()) {
|
||||
tv.layout(left, top, left + tv.getMeasuredWidth(), top + tv.getMeasuredHeight());
|
||||
} else {
|
||||
tv.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mTaskRect.top,
|
||||
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mTaskRect.bottom +
|
||||
tv.layout(mLayoutAlgorithm.mTaskRect.left, mLayoutAlgorithm.mTaskRect.top,
|
||||
mLayoutAlgorithm.mTaskRect.right, mLayoutAlgorithm.mTaskRect.bottom +
|
||||
tv.getMaxFooterHeight());
|
||||
}
|
||||
}
|
||||
@ -649,8 +512,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
/** Handler for the first layout. */
|
||||
void onFirstLayout() {
|
||||
int offscreenY = mStackAlgorithm.mViewRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
|
||||
int offscreenY = mLayoutAlgorithm.mViewRect.bottom -
|
||||
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
|
||||
|
||||
// Find the launch target task
|
||||
Task launchTargetTask = null;
|
||||
@ -717,10 +580,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
ctx.currentTaskTransform = new TaskViewTransform();
|
||||
ctx.currentStackViewIndex = i;
|
||||
ctx.currentStackViewCount = childCount;
|
||||
ctx.currentTaskRect = mStackAlgorithm.mTaskRect;
|
||||
ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
|
||||
ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
|
||||
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
|
||||
mStackAlgorithm.getStackTransform(task, getStackScroll(), ctx.currentTaskTransform);
|
||||
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(), ctx.currentTaskTransform, null);
|
||||
tv.startEnterRecentsAnimation(ctx);
|
||||
}
|
||||
|
||||
@ -737,9 +600,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
/** Requests this task stacks to start it's exit-recents animation. */
|
||||
public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
|
||||
// Animate all the task views into view
|
||||
ctx.offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
|
||||
// Stop any scrolling
|
||||
mStackScroller.stopScroller();
|
||||
mStackScroller.stopBoundScrollAnimation();
|
||||
// Animate all the task views out of view
|
||||
ctx.offscreenTranslationY = mLayoutAlgorithm.mViewRect.bottom -
|
||||
(mLayoutAlgorithm.mTaskRect.top - mLayoutAlgorithm.mViewRect.top);
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView tv = (TaskView) getChildAt(i);
|
||||
@ -753,8 +619,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
/** Animates a task view in this stack as it launches. */
|
||||
public void startLaunchTaskAnimation(TaskView tv, final Runnable r) {
|
||||
Task launchTargetTask = tv.getTask();
|
||||
int offscreenTranslationY = mStackAlgorithm.mViewRect.bottom -
|
||||
(mStackAlgorithm.mTaskRect.top - mStackAlgorithm.mViewRect.top);
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
TaskView t = (TaskView) getChildAt(i);
|
||||
@ -788,17 +652,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
@Override
|
||||
public void onStackTaskAdded(TaskStack stack, Task t) {
|
||||
// Update the task offsets
|
||||
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask) {
|
||||
// Update the task offsets
|
||||
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
|
||||
// Remove the view associated with this task, we can't rely on updateTransforms
|
||||
// to work here because the task is no longer in the list
|
||||
TaskView tv = getChildViewForTask(removedTask);
|
||||
@ -811,8 +669,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
// Update the min/max scroll and animate other task views into their new positions
|
||||
updateMinMaxScroll(true);
|
||||
int movement = (int) mStackAlgorithm.getTaskOverlapHeight();
|
||||
requestSynchronizeStackViewsWithModel(Utilities.calculateTranslationAnimationDuration(movement));
|
||||
requestSynchronizeStackViewsWithModel(200);
|
||||
|
||||
// Update the new front most task
|
||||
if (newFrontMostTask != null) {
|
||||
@ -839,6 +696,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
@Override
|
||||
public void onStackFiltered(TaskStack newStack, final ArrayList<Task> curTasks,
|
||||
Task filteredTask) {
|
||||
/*
|
||||
// Stash the scroll and filtered task for us to restore to when we unfilter
|
||||
mStashedScroll = getStackScroll();
|
||||
|
||||
@ -847,11 +705,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
getStackTransforms(curTasks, getStackScroll(), null, true);
|
||||
|
||||
// Update the task offsets
|
||||
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
|
||||
// Scroll the item to the top of the stack (sans-peek) rect so that we can see it better
|
||||
updateMinMaxScroll(false);
|
||||
float overlapHeight = mStackAlgorithm.getTaskOverlapHeight();
|
||||
float overlapHeight = mLayoutAlgorithm.getTaskOverlapHeight();
|
||||
setStackScrollRaw((int) (newStack.indexOfTask(filteredTask) * overlapHeight));
|
||||
boundScrollRaw();
|
||||
|
||||
@ -865,16 +723,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
// Notify any callbacks
|
||||
mCb.onTaskStackFilterTriggered();
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStackUnfiltered(TaskStack newStack, final ArrayList<Task> curTasks) {
|
||||
/*
|
||||
// Calculate the current task transforms
|
||||
final ArrayList<TaskViewTransform> curTaskTransforms =
|
||||
getStackTransforms(curTasks, getStackScroll(), null, true);
|
||||
|
||||
// Update the task offsets
|
||||
mStackAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
mLayoutAlgorithm.updateTaskOffsets(mStack.getTasks());
|
||||
|
||||
// Restore the stashed scroll
|
||||
updateMinMaxScroll(false);
|
||||
@ -894,6 +754,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
// Notify any callbacks
|
||||
mCb.onTaskStackUnfilterTriggered();
|
||||
*/
|
||||
}
|
||||
|
||||
/**** ViewPoolConsumer Implementation ****/
|
||||
@ -1014,7 +875,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
|
||||
@Override
|
||||
public void onTaskViewClipStateChanged(TaskView tv) {
|
||||
invalidate(mStackAlgorithm.mStackRect);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1022,6 +883,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
}
|
||||
|
||||
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
|
||||
|
||||
@Override
|
||||
public void onScrollChanged(float p) {
|
||||
mUIDozeTrigger.poke();
|
||||
requestSynchronizeStackViewsWithModel();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
|
||||
|
||||
@Override
|
||||
|
@ -18,184 +18,276 @@ package com.android.systemui.recents.views;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
import com.android.systemui.recents.misc.Console;
|
||||
import com.android.systemui.recents.misc.Utilities;
|
||||
import com.android.systemui.recents.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/* The layout logic for a TaskStackView */
|
||||
/* The layout logic for a TaskStackView.
|
||||
*
|
||||
* We are using a curve that defines the curve of the tasks as that go back in the recents list.
|
||||
* The curve is defined such that at curve progress p = 0 is the end of the curve (the top of the
|
||||
* stack rect), and p = 1 at the start of the curve and the bottom of the stack rect.
|
||||
*/
|
||||
public class TaskStackViewLayoutAlgorithm {
|
||||
|
||||
// These are all going to change
|
||||
static final float StackOverlapPct = 0.65f; // The overlap height relative to the task height
|
||||
static final float StackPeekHeightPct = 0.075f; // The height of the peek space relative to the stack height
|
||||
static final float StackPeekMinScale = 0.8f; // The min scale of the last card in the peek area
|
||||
static final int StackPeekNumCards = 3; // The number of cards we see in the peek space
|
||||
static final float StackPeekMinScale = 0.825f; // The min scale of the last card in the peek area
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
// The various rects that define the stack view
|
||||
Rect mViewRect = new Rect();
|
||||
Rect mStackVisibleRect = new Rect();
|
||||
Rect mStackRect = new Rect();
|
||||
Rect mStackRectSansPeek = new Rect();
|
||||
Rect mTaskRect = new Rect();
|
||||
|
||||
// The min/max scroll
|
||||
int mMinScroll;
|
||||
int mMaxScroll;
|
||||
// The min/max scroll progress
|
||||
float mMinScrollP;
|
||||
float mMaxScrollP;
|
||||
float mInitialScrollP;
|
||||
int mWithinAffiliationOffset;
|
||||
int mBetweenAffiliationOffset;
|
||||
HashMap<Task.TaskKey, Float> mTaskProgressMap = new HashMap<Task.TaskKey, Float>();
|
||||
|
||||
HashMap<Task.TaskKey, Integer> mTaskOffsetMap = new HashMap<Task.TaskKey, Integer>();
|
||||
// Log function
|
||||
static final float XScale = 1.75f; // The large the XScale, the longer the flat area of the curve
|
||||
static final float LogBase = 300;
|
||||
static final int PrecisionSteps = 250;
|
||||
static float[] xp;
|
||||
static float[] px;
|
||||
|
||||
public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) {
|
||||
mConfig = config;
|
||||
mWithinAffiliationOffset = mConfig.taskBarHeight;
|
||||
mBetweenAffiliationOffset = 4 * mConfig.taskBarHeight;
|
||||
|
||||
// Precompute the path
|
||||
initializeCurve();
|
||||
}
|
||||
|
||||
/** Computes the stack and task rects */
|
||||
public void computeRects(ArrayList<Task> tasks, int windowWidth, int windowHeight,
|
||||
Rect taskStackBounds) {
|
||||
// Note: We let the stack view be the full height because we want the cards to go under the
|
||||
// navigation bar if possible. However, the stack rects which we use to calculate
|
||||
// max scroll, etc. need to take the nav bar into account
|
||||
|
||||
public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
|
||||
// Compute the stack rects
|
||||
mViewRect.set(0, 0, windowWidth, windowHeight);
|
||||
mStackRect.set(taskStackBounds);
|
||||
mStackVisibleRect.set(taskStackBounds);
|
||||
mStackVisibleRect.bottom = mViewRect.bottom;
|
||||
|
||||
int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
|
||||
int heightPadding = mConfig.taskStackTopPaddingPx;
|
||||
mStackRect.inset(widthPadding, heightPadding);
|
||||
mStackRectSansPeek.set(mStackRect);
|
||||
mStackRectSansPeek.top += StackPeekHeightPct * windowHeight;
|
||||
|
||||
// Compute the task rect
|
||||
int size = mStackRect.width();
|
||||
int left = mStackRect.left + (mStackRect.width() - size) / 2;
|
||||
mTaskRect.set(left, mStackRectSansPeek.top,
|
||||
left + size, mStackRectSansPeek.top + size);
|
||||
|
||||
// Update the task offsets once the size changes
|
||||
updateTaskOffsets(tasks);
|
||||
mTaskRect.set(left, mStackRect.top,
|
||||
left + size, mStackRect.top + size);
|
||||
}
|
||||
|
||||
/** Computes the minimum and maximum scroll progress values */
|
||||
void computeMinMaxScroll(ArrayList<Task> tasks) {
|
||||
// Compute the min and max scroll values
|
||||
int numTasks = Math.max(1, tasks.size());
|
||||
int taskHeight = mTaskRect.height();
|
||||
int stackHeight = mStackRectSansPeek.height();
|
||||
// Clear the progress map
|
||||
mTaskProgressMap.clear();
|
||||
|
||||
if (numTasks <= 1) {
|
||||
// If there is only one task, then center the task in the stack rect (sans peek)
|
||||
mMinScroll = mMaxScroll = -(stackHeight -
|
||||
(taskHeight + mConfig.taskViewLockToAppButtonHeight)) / 2;
|
||||
} else {
|
||||
int maxScrollHeight = getStackScrollForTaskIndex(tasks.get(tasks.size() - 1))
|
||||
+ taskHeight + mConfig.taskViewLockToAppButtonHeight;
|
||||
mMinScroll = Math.min(stackHeight, maxScrollHeight) - stackHeight;
|
||||
mMaxScroll = maxScrollHeight - stackHeight;
|
||||
// Return early if we have no tasks
|
||||
if (tasks.isEmpty()) {
|
||||
mMinScrollP = mMaxScrollP = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int taskHeight = mTaskRect.height();
|
||||
float pAtBottomOfStackRect = screenYToCurveProgress(mStackVisibleRect.bottom);
|
||||
float pWithinAffiliateOffset = pAtBottomOfStackRect -
|
||||
screenYToCurveProgress(mStackVisibleRect.bottom - mWithinAffiliationOffset);
|
||||
float pBetweenAffiliateOffset = pAtBottomOfStackRect -
|
||||
screenYToCurveProgress(mStackVisibleRect.bottom - mBetweenAffiliationOffset);
|
||||
float pTaskHeightOffset = pAtBottomOfStackRect -
|
||||
screenYToCurveProgress(mStackVisibleRect.bottom - taskHeight);
|
||||
float pNavBarOffset = pAtBottomOfStackRect -
|
||||
screenYToCurveProgress(mStackVisibleRect.bottom - (mStackVisibleRect.bottom - mStackRect.bottom));
|
||||
|
||||
// Update the task offsets
|
||||
float pAtBackMostCardTop = screenYToCurveProgress(mStackVisibleRect.top +
|
||||
(mStackVisibleRect.height() - taskHeight) / 2);
|
||||
float pAtFrontMostCardTop = pAtBackMostCardTop;
|
||||
float pAtSecondFrontMostCardTop = pAtBackMostCardTop;
|
||||
int taskCount = tasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
Task task = tasks.get(i);
|
||||
mTaskProgressMap.put(task.key, pAtFrontMostCardTop);
|
||||
|
||||
if (i < (taskCount - 1)) {
|
||||
// Increment the peek height
|
||||
float pPeek = task.group.isFrontMostTask(task) ? pBetweenAffiliateOffset :
|
||||
pWithinAffiliateOffset;
|
||||
pAtSecondFrontMostCardTop = pAtFrontMostCardTop;
|
||||
pAtFrontMostCardTop += pPeek;
|
||||
}
|
||||
}
|
||||
|
||||
mMinScrollP = 0f;
|
||||
mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
|
||||
mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
|
||||
}
|
||||
|
||||
/** Update/get the transform */
|
||||
public TaskViewTransform getStackTransform(Task task, int stackScroll, TaskViewTransform transformOut) {
|
||||
public TaskViewTransform getStackTransform(Task task, float stackScroll, TaskViewTransform transformOut,
|
||||
TaskViewTransform prevTransform) {
|
||||
// Return early if we have an invalid index
|
||||
if (task == null) {
|
||||
transformOut.reset();
|
||||
return transformOut;
|
||||
}
|
||||
return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
|
||||
return getStackTransform(mTaskProgressMap.get(task.key), stackScroll, transformOut, prevTransform);
|
||||
}
|
||||
|
||||
/** Update/get the transform */
|
||||
public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
|
||||
// Map the items to an continuous position relative to the specified scroll
|
||||
int numPeekCards = StackPeekNumCards;
|
||||
float overlapHeight = StackOverlapPct * mTaskRect.height();
|
||||
float peekHeight = StackPeekHeightPct * mStackRect.height();
|
||||
float t = (taskStackScroll - stackScroll) / overlapHeight;
|
||||
float boundedT = Math.max(t, -(numPeekCards + 1));
|
||||
|
||||
// Set the scale relative to its position
|
||||
int numFrontScaledCards = 3;
|
||||
float minScale = StackPeekMinScale;
|
||||
float scaleRange = 1f - minScale;
|
||||
float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
|
||||
float scale = Math.max(minScale, Math.min(1f, minScale +
|
||||
((boundedT + (numPeekCards + 1)) * scaleInc)));
|
||||
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
|
||||
// Account for the bar offsets being scaled?
|
||||
float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
|
||||
transformOut.scale = scale;
|
||||
|
||||
// Set the y translation
|
||||
if (boundedT < 0f) {
|
||||
transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
|
||||
numPeekCards) * peekHeight - scaleYOffset);
|
||||
} else {
|
||||
transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
|
||||
public TaskViewTransform getStackTransform(float taskProgress, float stackScroll, TaskViewTransform transformOut, TaskViewTransform prevTransform) {
|
||||
float pTaskRelative = taskProgress - stackScroll;
|
||||
float pBounded = Math.max(0, Math.min(pTaskRelative, 1f));
|
||||
// If the task top is outside of the bounds below the screen, then immediately reset it
|
||||
if (pTaskRelative > 1f) {
|
||||
transformOut.reset();
|
||||
return transformOut;
|
||||
}
|
||||
|
||||
// Set the z translation
|
||||
// The check for the top is trickier, since we want to show the next task if it is at all
|
||||
// visible, even if p < 0.
|
||||
if (pTaskRelative < 0f) {
|
||||
if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
|
||||
transformOut.reset();
|
||||
return transformOut;
|
||||
}
|
||||
}
|
||||
float scale = curveProgressToScale(pBounded);
|
||||
int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2);
|
||||
int minZ = mConfig.taskViewTranslationZMinPx;
|
||||
int incZ = mConfig.taskViewTranslationZIncrementPx;
|
||||
transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
|
||||
|
||||
// Set the alphas
|
||||
// transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
|
||||
transformOut.dismissAlpha = 1f;
|
||||
|
||||
// Update the rect and visibility
|
||||
int maxZ = mConfig.taskViewTranslationZMaxPx;
|
||||
transformOut.scale = scale;
|
||||
transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top -
|
||||
scaleYOffset;
|
||||
transformOut.translationZ = Math.max(minZ, minZ + (pBounded * (maxZ - minZ)));
|
||||
transformOut.rect.set(mTaskRect);
|
||||
if (t < -(numPeekCards + 1)) {
|
||||
transformOut.visible = false;
|
||||
} else {
|
||||
transformOut.rect.offset(0, transformOut.translationY);
|
||||
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
|
||||
transformOut.visible = Rect.intersects(mViewRect, transformOut.rect);
|
||||
}
|
||||
transformOut.t = t;
|
||||
transformOut.rect.offset(0, transformOut.translationY);
|
||||
Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
|
||||
transformOut.visible = true;
|
||||
transformOut.p = pTaskRelative;
|
||||
return transformOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the overlap between one task and the next.
|
||||
* Returns the scroll to such task top = 1f;
|
||||
*/
|
||||
float getTaskOverlapHeight() {
|
||||
return StackOverlapPct * mTaskRect.height();
|
||||
float getStackScrollForTaskIndex(Task t) {
|
||||
return mTaskProgressMap.get(t.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scroll to such that the task transform at that index will have t=0. (If the scroll
|
||||
* is not bounded)
|
||||
*/
|
||||
int getStackScrollForTaskIndex(Task t) {
|
||||
return mTaskOffsetMap.get(t.key);
|
||||
}
|
||||
/** Initializes the curve. */
|
||||
public static void initializeCurve() {
|
||||
if (xp != null && px != null) return;
|
||||
xp = new float[PrecisionSteps + 1];
|
||||
px = new float[PrecisionSteps + 1];
|
||||
|
||||
/**
|
||||
* Returns the scroll to such that the task transform at that task + index will have t=0.
|
||||
* (If the scroll is not bounded)
|
||||
*/
|
||||
int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
|
||||
return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cache of tasks to offsets.
|
||||
*/
|
||||
void updateTaskOffsets(ArrayList<Task> tasks) {
|
||||
mTaskOffsetMap.clear();
|
||||
int offset = 0;
|
||||
int taskCount = tasks.size();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
Task t = tasks.get(i);
|
||||
mTaskOffsetMap.put(t.key, offset);
|
||||
if (t.group.isFrontMostTask(t)) {
|
||||
offset += getTaskOverlapHeight();
|
||||
} else {
|
||||
offset += mConfig.taskBarHeight;
|
||||
// Approximate f(x)
|
||||
float[] fx = new float[PrecisionSteps + 1];
|
||||
float step = 1f / PrecisionSteps;
|
||||
float x = 0;
|
||||
for (int xStep = 0; xStep <= PrecisionSteps; xStep++) {
|
||||
fx[xStep] = logFunc(x);
|
||||
x += step;
|
||||
}
|
||||
// Calculate the arc length for x:1->0
|
||||
float pLength = 0;
|
||||
float[] dx = new float[PrecisionSteps + 1];
|
||||
dx[0] = 0;
|
||||
for (int xStep = 1; xStep < PrecisionSteps; xStep++) {
|
||||
dx[xStep] = (float) Math.sqrt(Math.pow(fx[xStep] - fx[xStep - 1], 2) + Math.pow(step, 2));
|
||||
pLength += dx[xStep];
|
||||
}
|
||||
// Approximate p(x), a function of cumulative progress with x, normalized to 0..1
|
||||
float p = 0;
|
||||
px[0] = 0f;
|
||||
px[PrecisionSteps] = 1f;
|
||||
for (int xStep = 1; xStep <= PrecisionSteps; xStep++) {
|
||||
p += Math.abs(dx[xStep] / pLength);
|
||||
px[xStep] = p;
|
||||
}
|
||||
// Given p(x), calculate the inverse function x(p). This assumes that x(p) is also a valid
|
||||
// function.
|
||||
int xStep = 0;
|
||||
p = 0;
|
||||
xp[0] = 0f;
|
||||
xp[PrecisionSteps] = 1f;
|
||||
for (int pStep = 0; pStep < PrecisionSteps; pStep++) {
|
||||
// Walk forward in px and find the x where px <= p && p < px+1
|
||||
while (xStep < PrecisionSteps) {
|
||||
if (px[xStep] > p) break;
|
||||
xStep++;
|
||||
}
|
||||
// Now, px[xStep-1] <= p < px[xStep]
|
||||
if (xStep == 0) {
|
||||
xp[pStep] = 0;
|
||||
} else {
|
||||
// Find x such that proportionally, x is correct
|
||||
float fraction = (p - px[xStep - 1]) / (px[xStep] - px[xStep - 1]);
|
||||
x = (xStep - 1 + fraction) * step;
|
||||
xp[pStep] = x;
|
||||
}
|
||||
p += step;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reverses and scales out x. */
|
||||
static float reverse(float x) {
|
||||
return (-x * XScale) + 1;
|
||||
}
|
||||
/** The log function describing the curve. */
|
||||
static float logFunc(float x) {
|
||||
return 1f - (float) (Math.pow(LogBase, reverse(x))) / (LogBase);
|
||||
}
|
||||
/** The inverse of the log function describing the curve. */
|
||||
float invLogFunc(float y) {
|
||||
return (float) (Math.log((1f - reverse(y)) * (LogBase - 1) + 1) / Math.log(LogBase));
|
||||
}
|
||||
|
||||
/** Converts from the progress along the curve to a screen coordinate. */
|
||||
int curveProgressToScreenY(float p) {
|
||||
if (p < 0 || p > 1) return mStackVisibleRect.top + (int) (p * mStackVisibleRect.height());
|
||||
float pIndex = p * PrecisionSteps;
|
||||
int pFloorIndex = (int) Math.floor(pIndex);
|
||||
int pCeilIndex = (int) Math.ceil(pIndex);
|
||||
float xFraction = 0;
|
||||
if (pFloorIndex < PrecisionSteps && (pCeilIndex != pFloorIndex)) {
|
||||
float pFraction = (pIndex - pFloorIndex) / (pCeilIndex - pFloorIndex);
|
||||
xFraction = (xp[pCeilIndex] - xp[pFloorIndex]) * pFraction;
|
||||
}
|
||||
float x = xp[pFloorIndex] + xFraction;
|
||||
return mStackVisibleRect.top + (int) (x * mStackVisibleRect.height());
|
||||
}
|
||||
|
||||
/** Converts from the progress along the curve to a scale. */
|
||||
float curveProgressToScale(float p) {
|
||||
if (p < 0) return StackPeekMinScale;
|
||||
if (p > 1) return 1f;
|
||||
float scaleRange = (1f - StackPeekMinScale);
|
||||
float scale = StackPeekMinScale + (p * scaleRange);
|
||||
return scale;
|
||||
}
|
||||
|
||||
/** Converts from a screen coordinate to the progress along the curve. */
|
||||
float screenYToCurveProgress(int screenY) {
|
||||
float x = (float) (screenY - mStackVisibleRect.top) / mStackVisibleRect.height();
|
||||
if (x < 0 || x > 1) return x;
|
||||
float xIndex = x * PrecisionSteps;
|
||||
int xFloorIndex = (int) Math.floor(xIndex);
|
||||
int xCeilIndex = (int) Math.ceil(xIndex);
|
||||
float pFraction = 0;
|
||||
if (xFloorIndex < PrecisionSteps && (xCeilIndex != xFloorIndex)) {
|
||||
float xFraction = (xIndex - xFloorIndex) / (xCeilIndex - xFloorIndex);
|
||||
pFraction = (px[xCeilIndex] - px[xFloorIndex]) * xFraction;
|
||||
}
|
||||
return px[xFloorIndex] + pFraction;
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.recents.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.widget.OverScroller;
|
||||
import com.android.systemui.recents.RecentsConfiguration;
|
||||
|
||||
/* The scrolling logic for a TaskStackView */
|
||||
public class TaskStackViewScroller {
|
||||
public interface TaskStackViewScrollerCallbacks {
|
||||
public void onScrollChanged(float p);
|
||||
}
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
|
||||
TaskStackViewScrollerCallbacks mCb;
|
||||
|
||||
float mStackScrollP;
|
||||
|
||||
OverScroller mScroller;
|
||||
ObjectAnimator mScrollAnimator;
|
||||
|
||||
public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
|
||||
mConfig = config;
|
||||
mScroller = new OverScroller(context);
|
||||
mLayoutAlgorithm = layoutAlgorithm;
|
||||
setStackScroll(getStackScroll());
|
||||
}
|
||||
|
||||
/** Sets the callbacks */
|
||||
void setCallbacks(TaskStackViewScrollerCallbacks cb) {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
/** Gets the current stack scroll */
|
||||
public float getStackScroll() {
|
||||
return mStackScrollP;
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll */
|
||||
public void setStackScroll(float s) {
|
||||
mStackScrollP = s;
|
||||
if (mCb != null) {
|
||||
mCb.onScrollChanged(mStackScrollP);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll without calling the callback. */
|
||||
void setStackScrollRaw(float s) {
|
||||
mStackScrollP = s;
|
||||
}
|
||||
|
||||
/** Sets the current stack scroll to the initial state when you first enter recents */
|
||||
public void setStackScrollToInitialState() {
|
||||
setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
|
||||
}
|
||||
|
||||
/** Bounds the current scroll if necessary */
|
||||
public boolean boundScroll() {
|
||||
float curScroll = getStackScroll();
|
||||
float newScroll = getBoundedStackScroll(curScroll);
|
||||
if (Float.compare(newScroll, curScroll) != 0) {
|
||||
setStackScroll(newScroll);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
|
||||
public boolean boundScrollRaw() {
|
||||
float curScroll = getStackScroll();
|
||||
float newScroll = getBoundedStackScroll(curScroll);
|
||||
if (Float.compare(newScroll, curScroll) != 0) {
|
||||
setStackScrollRaw(newScroll);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the bounded stack scroll */
|
||||
float getBoundedStackScroll(float scroll) {
|
||||
return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
|
||||
}
|
||||
|
||||
/** Returns the amount that the aboslute value of how much the scroll is out of bounds. */
|
||||
float getScrollAmountOutOfBounds(float scroll) {
|
||||
if (scroll < mLayoutAlgorithm.mMinScrollP) {
|
||||
return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
|
||||
} else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
|
||||
return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/** Returns whether the specified scroll is out of bounds */
|
||||
boolean isScrollOutOfBounds() {
|
||||
return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
|
||||
}
|
||||
|
||||
/** Animates the stack scroll into bounds */
|
||||
ObjectAnimator animateBoundScroll() {
|
||||
float curScroll = getStackScroll();
|
||||
float newScroll = getBoundedStackScroll(curScroll);
|
||||
if (Float.compare(newScroll, curScroll) != 0) {
|
||||
// Start a new scroll animation
|
||||
animateScroll(curScroll, newScroll, null);
|
||||
}
|
||||
return mScrollAnimator;
|
||||
}
|
||||
|
||||
/** Animates the stack scroll */
|
||||
void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
|
||||
// Abort any current animations
|
||||
stopScroller();
|
||||
stopBoundScrollAnimation();
|
||||
|
||||
mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
|
||||
mScrollAnimator.setDuration(250);
|
||||
// We would have to project the difference into the screen coords, and then use that as the
|
||||
// duration
|
||||
// mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
|
||||
// curScroll, 250));
|
||||
mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
|
||||
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setStackScroll((Float) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
mScrollAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (postRunnable != null) {
|
||||
postRunnable.run();
|
||||
}
|
||||
mScrollAnimator.removeAllListeners();
|
||||
}
|
||||
});
|
||||
mScrollAnimator.start();
|
||||
}
|
||||
|
||||
/** Aborts any current stack scrolls */
|
||||
void stopBoundScrollAnimation() {
|
||||
if (mScrollAnimator != null) {
|
||||
mScrollAnimator.removeAllListeners();
|
||||
mScrollAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**** OverScroller ****/
|
||||
|
||||
int progressToScrollRange(float p) {
|
||||
return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
|
||||
}
|
||||
|
||||
float scrollRangeToProgress(int s) {
|
||||
return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
|
||||
}
|
||||
|
||||
/** Called from the view draw, computes the next scroll. */
|
||||
boolean computeScroll() {
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
float scroll = scrollRangeToProgress(mScroller.getCurrY());
|
||||
setStackScrollRaw(scroll);
|
||||
if (mCb != null) {
|
||||
mCb.onScrollChanged(scroll);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns whether the overscroller is scrolling. */
|
||||
boolean isScrolling() {
|
||||
return !mScroller.isFinished();
|
||||
}
|
||||
|
||||
/** Stops the scroller and any current fling. */
|
||||
void stopScroller() {
|
||||
if (!mScroller.isFinished()) {
|
||||
mScroller.abortAnimation();
|
||||
}
|
||||
}
|
||||
}
|
@ -29,16 +29,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
static int INACTIVE_POINTER_ID = -1;
|
||||
|
||||
TaskStackView mSv;
|
||||
TaskStackViewScroller mScroller;
|
||||
VelocityTracker mVelocityTracker;
|
||||
|
||||
boolean mIsScrolling;
|
||||
|
||||
float mInitialP;
|
||||
float mLastP;
|
||||
float mTotalPMotion;
|
||||
int mInitialMotionX, mInitialMotionY;
|
||||
int mLastMotionX, mLastMotionY;
|
||||
int mActivePointerId = INACTIVE_POINTER_ID;
|
||||
TaskView mActiveTaskView = null;
|
||||
|
||||
int mTotalScrollMotion;
|
||||
int mMinimumVelocity;
|
||||
int mMaximumVelocity;
|
||||
// The scroll touch slop is used to calculate when we start scrolling
|
||||
@ -49,14 +52,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
SwipeHelper mSwipeHelper;
|
||||
boolean mInterceptedBySwipeHelper;
|
||||
|
||||
public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
|
||||
public TaskStackViewTouchHandler(Context context, TaskStackView sv, TaskStackViewScroller scroller) {
|
||||
ViewConfiguration configuration = ViewConfiguration.get(context);
|
||||
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
|
||||
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
|
||||
mScrollTouchSlop = configuration.getScaledTouchSlop();
|
||||
mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
|
||||
mSv = sv;
|
||||
|
||||
mScroller = scroller;
|
||||
|
||||
float densityScale = context.getResources().getDisplayMetrics().density;
|
||||
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
|
||||
@ -97,6 +100,13 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Constructs a simulated motion event for the current stack scroll. */
|
||||
MotionEvent createMotionEventForStackScroll(MotionEvent ev) {
|
||||
MotionEvent pev = MotionEvent.obtainNoHistory(ev);
|
||||
pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll()));
|
||||
return pev;
|
||||
}
|
||||
|
||||
/** Touch preprocessing for handling below */
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
// Return early if we have no children
|
||||
@ -111,24 +121,25 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean wasScrolling = !mSv.mScroller.isFinished() ||
|
||||
(mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
|
||||
boolean wasScrolling = mScroller.isScrolling() ||
|
||||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
|
||||
int action = ev.getAction();
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
// Save the touch down info
|
||||
mInitialMotionX = mLastMotionX = (int) ev.getX();
|
||||
mInitialMotionY = mLastMotionY = (int) ev.getY();
|
||||
mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
|
||||
// Stop the current scroll if it is still flinging
|
||||
mSv.abortScroller();
|
||||
mSv.abortBoundScrollAnimation();
|
||||
mScroller.stopScroller();
|
||||
mScroller.stopBoundScrollAnimation();
|
||||
// Initialize the velocity tracker
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
|
||||
// Check if the scroller is finished yet
|
||||
mIsScrolling = !mSv.mScroller.isFinished();
|
||||
mIsScrolling = mScroller.isScrolling();
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
@ -142,7 +153,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
mIsScrolling = true;
|
||||
// Initialize the velocity tracker if necessary
|
||||
initVelocityTrackerIfNotExists();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
@ -152,17 +163,18 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
|
||||
mLastMotionX = x;
|
||||
mLastMotionY = y;
|
||||
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP: {
|
||||
// Animate the scroll back if we've cancelled
|
||||
mSv.animateBoundScroll();
|
||||
mScroller.animateBoundScroll();
|
||||
// Reset the drag state and the velocity tracker
|
||||
mIsScrolling = false;
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mActiveTaskView = null;
|
||||
mTotalScrollMotion = 0;
|
||||
mTotalPMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
break;
|
||||
}
|
||||
@ -186,7 +198,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
|
||||
// Update the velocity tracker
|
||||
initVelocityTrackerIfNotExists();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
int action = ev.getAction();
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
@ -194,14 +205,15 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
// Save the touch down info
|
||||
mInitialMotionX = mLastMotionX = (int) ev.getX();
|
||||
mInitialMotionY = mLastMotionY = (int) ev.getY();
|
||||
mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
|
||||
// Stop the current scroll if it is still flinging
|
||||
mSv.abortScroller();
|
||||
mSv.abortBoundScrollAnimation();
|
||||
mScroller.stopScroller();
|
||||
mScroller.stopBoundScrollAnimation();
|
||||
// Initialize the velocity tracker
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
@ -214,6 +226,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
mActivePointerId = ev.getPointerId(index);
|
||||
mLastMotionX = (int) ev.getX(index);
|
||||
mLastMotionY = (int) ev.getY(index);
|
||||
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
@ -223,13 +236,14 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
int x = (int) ev.getX(activePointerIndex);
|
||||
int y = (int) ev.getY(activePointerIndex);
|
||||
int yTotal = Math.abs(y - mInitialMotionY);
|
||||
int deltaY = mLastMotionY - y;
|
||||
float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y);
|
||||
float deltaP = mLastP - curP;
|
||||
if (!mIsScrolling) {
|
||||
if (yTotal > mScrollTouchSlop) {
|
||||
mIsScrolling = true;
|
||||
// Initialize the velocity tracker
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
|
||||
// Disallow parents from intercepting touch events
|
||||
final ViewParent parent = mSv.getParent();
|
||||
if (parent != null) {
|
||||
@ -238,23 +252,26 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
}
|
||||
}
|
||||
if (mIsScrolling) {
|
||||
int curStackScroll = mSv.getStackScroll();
|
||||
int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
|
||||
if (overScrollAmount != 0) {
|
||||
float curStackScroll = mScroller.getStackScroll();
|
||||
float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP);
|
||||
if (Float.compare(overScrollAmount, 0f) != 0) {
|
||||
// Bound the overscroll to a fixed amount, and inversely scale the y-movement
|
||||
// relative to how close we are to the max overscroll
|
||||
float maxOverScroll = mSv.mStackAlgorithm.mTaskRect.height() / 3f;
|
||||
deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
|
||||
/ maxOverScroll)));
|
||||
float maxOverScroll = 0.25f;
|
||||
deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount)
|
||||
/ maxOverScroll));
|
||||
}
|
||||
mSv.setStackScroll(curStackScroll + deltaY);
|
||||
if (mSv.isScrollOutOfBounds()) {
|
||||
mScroller.setStackScroll(curStackScroll + deltaP);
|
||||
if (mScroller.isScrollOutOfBounds()) {
|
||||
mVelocityTracker.clear();
|
||||
} else {
|
||||
mVelocityTracker.addMovement(createMotionEventForStackScroll(ev));
|
||||
}
|
||||
}
|
||||
mLastMotionX = x;
|
||||
mLastMotionY = y;
|
||||
mTotalScrollMotion += Math.abs(deltaY);
|
||||
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
mTotalPMotion += Math.abs(deltaP);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
@ -263,25 +280,27 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
|
||||
|
||||
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
|
||||
// XXX: Should this be calculated as a percentage of a curve?
|
||||
int overscrollRange = (int) (Math.min(1f,
|
||||
Math.abs((float) velocity / mMaximumVelocity)) *
|
||||
Constants.Values.TaskStackView.TaskStackOverscrollRange);
|
||||
// Fling scroll
|
||||
mSv.mScroller.fling(0, mSv.getStackScroll(),
|
||||
0, -velocity,
|
||||
mScroller.mScroller.fling(0, mScroller.progressToScrollRange(mScroller.getStackScroll()),
|
||||
0, velocity,
|
||||
0, 0,
|
||||
mSv.mMinScroll, mSv.mMaxScroll,
|
||||
mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP),
|
||||
mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP),
|
||||
0, overscrollRange);
|
||||
// Invalidate to kick off computeScroll
|
||||
mSv.invalidate(mSv.mStackAlgorithm.mStackRect);
|
||||
} else if (mSv.isScrollOutOfBounds()) {
|
||||
mSv.invalidate();
|
||||
} else if (mScroller.isScrollOutOfBounds()) {
|
||||
// Animate the scroll back into bounds
|
||||
mSv.animateBoundScroll();
|
||||
mScroller.animateBoundScroll();
|
||||
}
|
||||
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mIsScrolling = false;
|
||||
mTotalScrollMotion = 0;
|
||||
mTotalPMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
break;
|
||||
}
|
||||
@ -294,18 +313,19 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
mLastMotionX = (int) ev.getX(newPointerIndex);
|
||||
mLastMotionY = (int) ev.getY(newPointerIndex);
|
||||
mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY);
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
if (mSv.isScrollOutOfBounds()) {
|
||||
if (mScroller.isScrollOutOfBounds()) {
|
||||
// Animate the scroll back into bounds
|
||||
mSv.animateBoundScroll();
|
||||
mScroller.animateBoundScroll();
|
||||
}
|
||||
mActivePointerId = INACTIVE_POINTER_ID;
|
||||
mIsScrolling = false;
|
||||
mTotalScrollMotion = 0;
|
||||
mTotalPMotion = 0;
|
||||
recycleVelocityTracker();
|
||||
break;
|
||||
}
|
||||
|
@ -52,9 +52,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
|
||||
RecentsConfiguration mConfig;
|
||||
|
||||
float mTaskProgress;
|
||||
ObjectAnimator mTaskProgressAnimator;
|
||||
float mMaxDimScale;
|
||||
int mDim;
|
||||
int mMaxDim;
|
||||
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
|
||||
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator(1.25f);
|
||||
PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
Task mTask;
|
||||
@ -76,7 +78,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
updateDimOverlayFromScale();
|
||||
setTaskProgress((Float) animation.getAnimatedValue());
|
||||
}
|
||||
};
|
||||
|
||||
@ -96,10 +98,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mConfig = RecentsConfiguration.getInstance();
|
||||
mMaxDim = mConfig.taskStackMaxDim;
|
||||
mMaxDimScale = mConfig.taskStackMaxDim / 255f;
|
||||
mClipViewInStack = true;
|
||||
mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
|
||||
setOutlineProvider(mViewBounds);
|
||||
setTaskProgress(getTaskProgress());
|
||||
setDim(getDim());
|
||||
}
|
||||
|
||||
@ -159,9 +162,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
|
||||
/** Synchronizes this view's properties with the task's transform */
|
||||
void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
|
||||
// Update the bar view
|
||||
mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
|
||||
|
||||
// If we are a full screen view, then only update the Z to keep it in order
|
||||
// XXX: Also update/animate the dim as well
|
||||
if (mIsFullScreenView) {
|
||||
@ -173,8 +173,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
}
|
||||
|
||||
// Apply the transform
|
||||
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
|
||||
mUpdateDimListener);
|
||||
toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false);
|
||||
|
||||
// Update the task progress
|
||||
if (mTaskProgressAnimator != null) {
|
||||
mTaskProgressAnimator.removeAllListeners();
|
||||
mTaskProgressAnimator.cancel();
|
||||
}
|
||||
if (duration <= 0) {
|
||||
setTaskProgress(toTransform.p);
|
||||
} else {
|
||||
mTaskProgressAnimator = ObjectAnimator.ofFloat(this, "taskProgress", toTransform.p);
|
||||
mTaskProgressAnimator.setDuration(duration);
|
||||
mTaskProgressAnimator.addUpdateListener(mUpdateDimListener);
|
||||
mTaskProgressAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets this view's properties */
|
||||
@ -325,7 +338,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
mThumbnailView.enableTaskBarClipAsRunnable(mBarView));
|
||||
|
||||
// Animate the dim into view as well
|
||||
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
|
||||
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimFromTaskProgress());
|
||||
anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
|
||||
anim.setDuration(mConfig.taskBarEnterAnimDuration);
|
||||
anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
|
||||
@ -556,6 +569,17 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the current task progress. */
|
||||
public void setTaskProgress(float p) {
|
||||
mTaskProgress = p;
|
||||
updateDimFromTaskProgress();
|
||||
}
|
||||
|
||||
/** Returns the current task progress. */
|
||||
public float getTaskProgress() {
|
||||
return mTaskProgress;
|
||||
}
|
||||
|
||||
/** Returns the current dim. */
|
||||
public void setDim(int dim) {
|
||||
mDim = dim;
|
||||
@ -571,17 +595,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
}
|
||||
|
||||
/** Compute the dim as a function of the scale of this view. */
|
||||
int getDimOverlayFromScale() {
|
||||
float minScale = TaskStackViewLayoutAlgorithm.StackPeekMinScale;
|
||||
float scaleRange = 1f - minScale;
|
||||
float dim = (1f - getScaleX()) / scaleRange;
|
||||
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
|
||||
return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
|
||||
int getDimFromTaskProgress() {
|
||||
float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
|
||||
return (int) (dim * 255);
|
||||
}
|
||||
|
||||
/** Update the dim as a function of the scale of this view. */
|
||||
void updateDimOverlayFromScale() {
|
||||
setDim(getDimOverlayFromScale());
|
||||
void updateDimFromTaskProgress() {
|
||||
setDim(getDimFromTaskProgress());
|
||||
}
|
||||
|
||||
/**** View focus state ****/
|
||||
@ -650,9 +671,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
}
|
||||
mBarView.rebindToTask(mTask);
|
||||
// Rebind any listeners
|
||||
if (Constants.DebugFlags.App.EnableTaskFiltering) {
|
||||
mBarView.mApplicationIcon.setOnClickListener(this);
|
||||
}
|
||||
mBarView.mApplicationIcon.setOnClickListener(this);
|
||||
mBarView.mDismissButton.setOnClickListener(this);
|
||||
if (mFooterView != null) {
|
||||
mFooterView.setOnClickListener(this);
|
||||
@ -675,9 +694,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
mThumbnailView.unbindFromTask();
|
||||
mBarView.unbindFromTask();
|
||||
// Unbind any listeners
|
||||
if (Constants.DebugFlags.App.EnableTaskFiltering) {
|
||||
mBarView.mApplicationIcon.setOnClickListener(null);
|
||||
}
|
||||
mBarView.mApplicationIcon.setOnClickListener(null);
|
||||
mBarView.mDismissButton.setOnClickListener(null);
|
||||
if (mFooterView != null) {
|
||||
mFooterView.setOnClickListener(null);
|
||||
@ -717,7 +734,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (v == mBarView.mApplicationIcon) {
|
||||
if (Constants.DebugFlags.App.EnableTaskFiltering && v == mBarView.mApplicationIcon) {
|
||||
mCb.onTaskViewAppIconClicked(tv);
|
||||
} else if (v == mBarView.mDismissButton) {
|
||||
// Animate out the view and call the callback
|
||||
@ -729,7 +746,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
|
||||
});
|
||||
// Hide the footer
|
||||
tv.animateFooterVisibility(false, mConfig.taskViewRemoveAnimDuration);
|
||||
} else if (v == tv || (v == mFooterView || v == mActionButtonView)) {
|
||||
} else {
|
||||
mCb.onTaskViewClicked(tv, tv.getTask(),
|
||||
(v == mFooterView || v == mActionButtonView));
|
||||
}
|
||||
|
@ -28,13 +28,12 @@ import com.android.systemui.recents.Constants;
|
||||
public class TaskViewTransform {
|
||||
public int startDelay = 0;
|
||||
public int translationY = 0;
|
||||
public int translationZ = 0;
|
||||
public float translationZ = 0;
|
||||
public float scale = 1f;
|
||||
public float alpha = 1f;
|
||||
public float dismissAlpha = 1f;
|
||||
public boolean visible = false;
|
||||
public Rect rect = new Rect();
|
||||
float t = 0f;
|
||||
float p = 0f;
|
||||
|
||||
public TaskViewTransform() {
|
||||
// Do nothing
|
||||
@ -46,10 +45,9 @@ public class TaskViewTransform {
|
||||
translationZ = o.translationZ;
|
||||
scale = o.scale;
|
||||
alpha = o.alpha;
|
||||
dismissAlpha = o.dismissAlpha;
|
||||
visible = o.visible;
|
||||
rect.set(o.rect);
|
||||
t = o.t;
|
||||
p = o.p;
|
||||
}
|
||||
|
||||
/** Resets the current transform */
|
||||
@ -59,19 +57,15 @@ public class TaskViewTransform {
|
||||
translationZ = 0;
|
||||
scale = 1f;
|
||||
alpha = 1f;
|
||||
dismissAlpha = 1f;
|
||||
visible = false;
|
||||
rect.setEmpty();
|
||||
t = 0f;
|
||||
p = 0f;
|
||||
}
|
||||
|
||||
/** Convenience functions to compare against current property values */
|
||||
public boolean hasAlphaChangedFrom(float v) {
|
||||
return (Float.compare(alpha, v) != 0);
|
||||
}
|
||||
public boolean hasDismissAlphaChangedFrom(float v) {
|
||||
return (Float.compare(dismissAlpha, v) != 0);
|
||||
}
|
||||
public boolean hasScaleChangedFrom(float v) {
|
||||
return (Float.compare(scale, v) != 0);
|
||||
}
|
||||
@ -83,8 +77,7 @@ public class TaskViewTransform {
|
||||
}
|
||||
|
||||
/** Applies this transform to a view. */
|
||||
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
|
||||
ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
|
||||
public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers) {
|
||||
// Check to see if any properties have changed, and update the task view
|
||||
if (duration > 0) {
|
||||
ViewPropertyAnimator anim = v.animate();
|
||||
@ -100,8 +93,7 @@ public class TaskViewTransform {
|
||||
}
|
||||
if (hasScaleChangedFrom(v.getScaleX())) {
|
||||
anim.scaleX(scale)
|
||||
.scaleY(scale)
|
||||
.setUpdateListener(scaleUpdateListener);
|
||||
.scaleY(scale);
|
||||
requiresLayers = true;
|
||||
}
|
||||
if (hasAlphaChangedFrom(v.getAlpha())) {
|
||||
@ -128,7 +120,6 @@ public class TaskViewTransform {
|
||||
if (hasScaleChangedFrom(v.getScaleX())) {
|
||||
v.setScaleX(scale);
|
||||
v.setScaleY(scale);
|
||||
scaleUpdateListener.onAnimationUpdate(null);
|
||||
}
|
||||
if (hasAlphaChangedFrom(v.getAlpha())) {
|
||||
v.setAlpha(alpha);
|
||||
@ -152,6 +143,6 @@ public class TaskViewTransform {
|
||||
public String toString() {
|
||||
return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
|
||||
" scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
|
||||
" dismissAlpha: " + dismissAlpha;
|
||||
" p: " + p;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user