blob: 32f02acd9d7782a513ba8ad9359dded35fa87c64 [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() {
154 float curScroll = getStackScroll();
155 float newScroll = getBoundedStackScroll(curScroll);
156 if (Float.compare(newScroll, curScroll) != 0) {
157 // Start a new scroll animation
158 animateScroll(curScroll, newScroll, null);
159 }
160 return mScrollAnimator;
161 }
162
163 /** Animates the stack scroll */
164 void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
Winson Chungd213a1e2014-10-02 11:18:30 -0700165 // Finish any current scrolling animations
166 if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
167 setStackScroll(mFinalAnimatedScroll);
168 mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
169 }
Winson Chung012ef362014-07-31 18:36:25 -0700170 stopScroller();
171 stopBoundScrollAnimation();
172
Winson Chungd213a1e2014-10-02 11:18:30 -0700173 mFinalAnimatedScroll = newScroll;
Winson Chung012ef362014-07-31 18:36:25 -0700174 mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
Winson35f30502015-09-28 11:24:36 -0700175 mScrollAnimator.setDuration(mContext.getResources().getInteger(
176 R.integer.recents_animate_task_stack_scroll_duration));
177 mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
Winson Chung012ef362014-07-31 18:36:25 -0700178 mScrollAnimator.addListener(new AnimatorListenerAdapter() {
179 @Override
180 public void onAnimationEnd(Animator animation) {
181 if (postRunnable != null) {
182 postRunnable.run();
183 }
184 mScrollAnimator.removeAllListeners();
185 }
186 });
187 mScrollAnimator.start();
188 }
189
190 /** Aborts any current stack scrolls */
191 void stopBoundScrollAnimation() {
Winson Chung353c0b92014-10-16 17:43:23 -0700192 Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
Winson Chung012ef362014-07-31 18:36:25 -0700193 }
194
195 /**** OverScroller ****/
196
Winson23afcae2015-10-28 11:14:54 -0700197 // TODO: Remove
198 @Deprecated
Winson Chung012ef362014-07-31 18:36:25 -0700199 int progressToScrollRange(float p) {
Winsonf0d1c442015-12-01 11:04:45 -0800200 return (int) (p * mLayoutAlgorithm.mStackRect.height());
Winson Chung012ef362014-07-31 18:36:25 -0700201 }
202
Winson Chung012ef362014-07-31 18:36:25 -0700203 /** Called from the view draw, computes the next scroll. */
204 boolean computeScroll() {
205 if (mScroller.computeScrollOffset()) {
Winson23afcae2015-10-28 11:14:54 -0700206 float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
207 float scroll = mFlingDownScrollP + deltaP;
208 setStackScroll(scroll);
209 if (DEBUG) {
210 Log.d(TAG, "computeScroll: " + scroll);
Winson Chung012ef362014-07-31 18:36:25 -0700211 }
212 return true;
213 }
214 return false;
215 }
216
217 /** Returns whether the overscroller is scrolling. */
218 boolean isScrolling() {
219 return !mScroller.isFinished();
220 }
221
222 /** Stops the scroller and any current fling. */
223 void stopScroller() {
224 if (!mScroller.isFinished()) {
225 mScroller.abortAnimation();
226 }
227 }
228}