Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.systemui.recents.views; |
| 18 | |
| 19 | import android.animation.Animator; |
| 20 | import android.animation.AnimatorListenerAdapter; |
| 21 | import android.animation.ObjectAnimator; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 22 | import android.content.Context; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 23 | import android.util.Log; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 24 | import android.view.animation.AnimationUtils; |
| 25 | import android.view.animation.Interpolator; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 26 | import android.widget.OverScroller; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 27 | import com.android.systemui.R; |
Winson | 2536c7e | 2015-10-01 15:49:31 -0700 | [diff] [blame] | 28 | import com.android.systemui.recents.misc.Utilities; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 29 | |
| 30 | /* The scrolling logic for a TaskStackView */ |
| 31 | public class TaskStackViewScroller { |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 32 | |
| 33 | private static final String TAG = "TaskStackViewScroller"; |
| 34 | private static final boolean DEBUG = false; |
| 35 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 36 | public interface TaskStackViewScrollerCallbacks { |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 37 | void onScrollChanged(float prevScroll, float curScroll, TaskViewAnimation animation); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 38 | } |
| 39 | |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 40 | Context mContext; |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 41 | TaskStackLayoutAlgorithm mLayoutAlgorithm; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 42 | TaskStackViewScrollerCallbacks mCb; |
| 43 | |
| 44 | float mStackScrollP; |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 45 | float mFlingDownScrollP; |
| 46 | int mFlingDownY; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 47 | |
| 48 | OverScroller mScroller; |
| 49 | ObjectAnimator mScrollAnimator; |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 50 | float mFinalAnimatedScroll; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 51 | |
Winson | 116b2c2 | 2015-12-03 17:21:00 -0800 | [diff] [blame] | 52 | private Interpolator mLinearOutSlowInInterpolator; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 53 | |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 54 | public TaskStackViewScroller(Context context, TaskStackLayoutAlgorithm layoutAlgorithm) { |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 55 | mContext = context; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 56 | mScroller = new OverScroller(context); |
| 57 | mLayoutAlgorithm = layoutAlgorithm; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 58 | mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, |
| 59 | com.android.internal.R.interpolator.linear_out_slow_in); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 60 | } |
| 61 | |
Winson Chung | c041d88 | 2014-11-14 17:34:03 -0800 | [diff] [blame] | 62 | /** Resets the task scroller. */ |
| 63 | void reset() { |
| 64 | mStackScrollP = 0f; |
| 65 | } |
| 66 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 67 | /** 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 | |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 77 | /** |
| 78 | * Sets the current stack scroll immediately. |
| 79 | */ |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 80 | public void setStackScroll(float s) { |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 81 | 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) { |
Winson | c29ff00 | 2015-11-20 16:00:45 -0800 | [diff] [blame] | 89 | float prevStackScroll = mStackScrollP; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 90 | mStackScrollP = s; |
| 91 | if (mCb != null) { |
Winson | f24f216 | 2016-01-05 12:11:55 -0800 | [diff] [blame] | 92 | mCb.onScrollChanged(prevStackScroll, mStackScrollP, animation); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 93 | } |
| 94 | } |
| 95 | |
Winson Chung | c041d88 | 2014-11-14 17:34:03 -0800 | [diff] [blame] | 96 | /** |
| 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 102 | setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP)); |
Winson Chung | c041d88 | 2014-11-14 17:34:03 -0800 | [diff] [blame] | 103 | return Float.compare(prevStackScrollP, mStackScrollP) != 0; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 104 | } |
| 105 | |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 106 | /** |
| 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 120 | /** 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 130 | |
| 131 | /** Returns the bounded stack scroll */ |
| 132 | float getBoundedStackScroll(float scroll) { |
Winson | 36a5a2c | 2015-10-29 18:04:39 -0700 | [diff] [blame] | 133 | return Math.max(mLayoutAlgorithm.mMinScrollP, |
Winson | a5e6b36 | 2015-11-02 17:17:20 -0800 | [diff] [blame] | 134 | Math.min(mLayoutAlgorithm.mMaxScrollP, scroll)); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 135 | } |
| 136 | |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 137 | /** Returns the amount that the absolute value of how much the scroll is out of bounds. */ |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 138 | 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 Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 165 | // 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 170 | stopScroller(); |
| 171 | stopBoundScrollAnimation(); |
| 172 | |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 173 | mFinalAnimatedScroll = newScroll; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 174 | mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll); |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 175 | mScrollAnimator.setDuration(mContext.getResources().getInteger( |
| 176 | R.integer.recents_animate_task_stack_scroll_duration)); |
| 177 | mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 178 | 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 Chung | 353c0b9 | 2014-10-16 17:43:23 -0700 | [diff] [blame] | 192 | Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | /**** OverScroller ****/ |
| 196 | |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 197 | // TODO: Remove |
| 198 | @Deprecated |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 199 | int progressToScrollRange(float p) { |
Winson | f0d1c44 | 2015-12-01 11:04:45 -0800 | [diff] [blame] | 200 | return (int) (p * mLayoutAlgorithm.mStackRect.height()); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 201 | } |
| 202 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 203 | /** Called from the view draw, computes the next scroll. */ |
| 204 | boolean computeScroll() { |
| 205 | if (mScroller.computeScrollOffset()) { |
Winson | 23afcae | 2015-10-28 11:14:54 -0700 | [diff] [blame] | 206 | 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 Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 211 | } |
| 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 | } |