blob: 5335b14078dd60c038768cf612f667114245b88a [file] [log] [blame]
Winson Chung012ef362014-07-31 18:36:25 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.recents.views;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Winson Chung012ef362014-07-31 18:36:25 -070022import android.content.Context;
Winson23afcae2015-10-28 11:14:54 -070023import android.util.Log;
Winson35f30502015-09-28 11:24:36 -070024import android.view.animation.AnimationUtils;
25import android.view.animation.Interpolator;
Winson Chung012ef362014-07-31 18:36:25 -070026import android.widget.OverScroller;
Winson35f30502015-09-28 11:24:36 -070027import com.android.systemui.R;
Winson2536c7e2015-10-01 15:49:31 -070028import com.android.systemui.recents.misc.Utilities;
Winson Chung012ef362014-07-31 18:36:25 -070029
30/* The scrolling logic for a TaskStackView */
31public class TaskStackViewScroller {
Winson23afcae2015-10-28 11:14:54 -070032
33 private static final String TAG = "TaskStackViewScroller";
34 private static final boolean DEBUG = false;
35
Winson Chung012ef362014-07-31 18:36:25 -070036 public interface TaskStackViewScrollerCallbacks {
Winsonf24f2162016-01-05 12:11:55 -080037 void onScrollChanged(float prevScroll, float curScroll, TaskViewAnimation animation);
Winson Chung012ef362014-07-31 18:36:25 -070038 }
39
Winson35f30502015-09-28 11:24:36 -070040 Context mContext;
Winson36a5a2c2015-10-29 18:04:39 -070041 TaskStackLayoutAlgorithm mLayoutAlgorithm;
Winson Chung012ef362014-07-31 18:36:25 -070042 TaskStackViewScrollerCallbacks mCb;
43
44 float mStackScrollP;
Winson23afcae2015-10-28 11:14:54 -070045 float mFlingDownScrollP;
46 int mFlingDownY;
Winson Chung012ef362014-07-31 18:36:25 -070047
48 OverScroller mScroller;
49 ObjectAnimator mScrollAnimator;
Winson Chungd213a1e2014-10-02 11:18:30 -070050 float mFinalAnimatedScroll;
Winson Chung012ef362014-07-31 18:36:25 -070051
Winson116b2c22015-12-03 17:21:00 -080052 private Interpolator mLinearOutSlowInInterpolator;
Winson35f30502015-09-28 11:24:36 -070053
Winson36a5a2c2015-10-29 18:04:39 -070054 public TaskStackViewScroller(Context context, TaskStackLayoutAlgorithm layoutAlgorithm) {
Winson35f30502015-09-28 11:24:36 -070055 mContext = context;
Winson Chung012ef362014-07-31 18:36:25 -070056 mScroller = new OverScroller(context);
57 mLayoutAlgorithm = layoutAlgorithm;
Winson35f30502015-09-28 11:24:36 -070058 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
59 com.android.internal.R.interpolator.linear_out_slow_in);
Winson Chung012ef362014-07-31 18:36:25 -070060 }
61
Winson Chungc041d882014-11-14 17:34:03 -080062 /** Resets the task scroller. */
63 void reset() {
64 mStackScrollP = 0f;
65 }
66
Winson Chung012ef362014-07-31 18:36:25 -070067 /** Sets the callbacks */
68 void setCallbacks(TaskStackViewScrollerCallbacks cb) {
69 mCb = cb;
70 }
71
72 /** Gets the current stack scroll */
73 public float getStackScroll() {
74 return mStackScrollP;
75 }
76
Winsonf24f2162016-01-05 12:11:55 -080077 /**
78 * Sets the current stack scroll immediately.
79 */
Winson Chung012ef362014-07-31 18:36:25 -070080 public void setStackScroll(float s) {
Winsonf24f2162016-01-05 12:11:55 -080081 setStackScroll(s, TaskViewAnimation.IMMEDIATE);
82 }
83
84 /**
85 * Sets the current stack scroll, but indicates to the callback the preferred animation to
86 * update to this new scroll.
87 */
88 public void setStackScroll(float s, TaskViewAnimation animation) {
Winsonc29ff002015-11-20 16:00:45 -080089 float prevStackScroll = mStackScrollP;
Winson Chung012ef362014-07-31 18:36:25 -070090 mStackScrollP = s;
91 if (mCb != null) {
Winsonf24f2162016-01-05 12:11:55 -080092 mCb.onScrollChanged(prevStackScroll, mStackScrollP, animation);
Winson Chung012ef362014-07-31 18:36:25 -070093 }
94 }
95
Winson Chungc041d882014-11-14 17:34:03 -080096 /**
97 * Sets the current stack scroll to the initial state when you first enter recents.
98 * @return whether the stack progress changed.
99 */
100 public boolean setStackScrollToInitialState() {
101 float prevStackScrollP = mStackScrollP;
Winson Chung012ef362014-07-31 18:36:25 -0700102 setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
Winson Chungc041d882014-11-14 17:34:03 -0800103 return Float.compare(prevStackScrollP, mStackScrollP) != 0;
Winson Chung012ef362014-07-31 18:36:25 -0700104 }
105
Winson23afcae2015-10-28 11:14:54 -0700106 /**
107 * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
108 */
109 public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
110 int overscroll) {
111 if (DEBUG) {
112 Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
113 ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
114 }
115 mFlingDownScrollP = downScrollP;
116 mFlingDownY = downY;
117 mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
118 }
119
Winson Chung012ef362014-07-31 18:36:25 -0700120 /** Bounds the current scroll if necessary */
121 public boolean boundScroll() {
122 float curScroll = getStackScroll();
123 float newScroll = getBoundedStackScroll(curScroll);
124 if (Float.compare(newScroll, curScroll) != 0) {
125 setStackScroll(newScroll);
126 return true;
127 }
128 return false;
129 }
Winson Chung012ef362014-07-31 18:36:25 -0700130
131 /** Returns the bounded stack scroll */
132 float getBoundedStackScroll(float scroll) {
Winson36a5a2c2015-10-29 18:04:39 -0700133 return Math.max(mLayoutAlgorithm.mMinScrollP,
Winsona5e6b362015-11-02 17:17:20 -0800134 Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
Winson Chung012ef362014-07-31 18:36:25 -0700135 }
136
Winson Chung6ac8bd62015-01-07 16:38:35 -0800137 /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
Winson Chung012ef362014-07-31 18:36:25 -0700138 float getScrollAmountOutOfBounds(float scroll) {
139 if (scroll < mLayoutAlgorithm.mMinScrollP) {
140 return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
141 } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
142 return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
143 }
144 return 0f;
145 }
146
147 /** Returns whether the specified scroll is out of bounds */
148 boolean isScrollOutOfBounds() {
149 return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
150 }
151
152 /** Animates the stack scroll into bounds */
153 ObjectAnimator animateBoundScroll() {
Winson8aa99592016-01-19 15:07:07 -0800154 // TODO: Take duration for snap back
Winson Chung012ef362014-07-31 18:36:25 -0700155 float curScroll = getStackScroll();
156 float newScroll = getBoundedStackScroll(curScroll);
157 if (Float.compare(newScroll, curScroll) != 0) {
158 // Start a new scroll animation
159 animateScroll(curScroll, newScroll, null);
160 }
161 return mScrollAnimator;
162 }
163
164 /** Animates the stack scroll */
165 void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
Winson Chungd213a1e2014-10-02 11:18:30 -0700166 // Finish any current scrolling animations
167 if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
168 setStackScroll(mFinalAnimatedScroll);
169 mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
170 }
Winson Chung012ef362014-07-31 18:36:25 -0700171 stopScroller();
172 stopBoundScrollAnimation();
173
Winson Chungd213a1e2014-10-02 11:18:30 -0700174 mFinalAnimatedScroll = newScroll;
Winson Chung012ef362014-07-31 18:36:25 -0700175 mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
Winson35f30502015-09-28 11:24:36 -0700176 mScrollAnimator.setDuration(mContext.getResources().getInteger(
177 R.integer.recents_animate_task_stack_scroll_duration));
178 mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
Winson Chung012ef362014-07-31 18:36:25 -0700179 mScrollAnimator.addListener(new AnimatorListenerAdapter() {
180 @Override
181 public void onAnimationEnd(Animator animation) {
182 if (postRunnable != null) {
183 postRunnable.run();
184 }
185 mScrollAnimator.removeAllListeners();
186 }
187 });
188 mScrollAnimator.start();
189 }
190
191 /** Aborts any current stack scrolls */
192 void stopBoundScrollAnimation() {
Winson Chung353c0b92014-10-16 17:43:23 -0700193 Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
Winson Chung012ef362014-07-31 18:36:25 -0700194 }
195
196 /**** OverScroller ****/
197
Winson23afcae2015-10-28 11:14:54 -0700198 // TODO: Remove
199 @Deprecated
Winson Chung012ef362014-07-31 18:36:25 -0700200 int progressToScrollRange(float p) {
Winsonf0d1c442015-12-01 11:04:45 -0800201 return (int) (p * mLayoutAlgorithm.mStackRect.height());
Winson Chung012ef362014-07-31 18:36:25 -0700202 }
203
Winson Chung012ef362014-07-31 18:36:25 -0700204 /** Called from the view draw, computes the next scroll. */
205 boolean computeScroll() {
206 if (mScroller.computeScrollOffset()) {
Winson23afcae2015-10-28 11:14:54 -0700207 float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
208 float scroll = mFlingDownScrollP + deltaP;
209 setStackScroll(scroll);
210 if (DEBUG) {
211 Log.d(TAG, "computeScroll: " + scroll);
Winson Chung012ef362014-07-31 18:36:25 -0700212 }
213 return true;
214 }
215 return false;
216 }
217
218 /** Returns whether the overscroller is scrolling. */
219 boolean isScrolling() {
220 return !mScroller.isFinished();
221 }
222
223 /** Stops the scroller and any current fling. */
224 void stopScroller() {
225 if (!mScroller.isFinished()) {
226 mScroller.abortAnimation();
227 }
228 }
229}