• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
22 import android.animation.ValueAnimator;
23 import android.content.Context;
24 import android.widget.OverScroller;
25 import com.android.systemui.recents.RecentsConfiguration;
26 import com.android.systemui.recents.misc.Utilities;
27 
28 /* The scrolling logic for a TaskStackView */
29 public class TaskStackViewScroller {
30     public interface TaskStackViewScrollerCallbacks {
onScrollChanged(float p)31         public void onScrollChanged(float p);
32     }
33 
34     RecentsConfiguration mConfig;
35     TaskStackViewLayoutAlgorithm mLayoutAlgorithm;
36     TaskStackViewScrollerCallbacks mCb;
37 
38     float mStackScrollP;
39 
40     OverScroller mScroller;
41     ObjectAnimator mScrollAnimator;
42     float mFinalAnimatedScroll;
43 
TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm)44     public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) {
45         mConfig = config;
46         mScroller = new OverScroller(context);
47         mLayoutAlgorithm = layoutAlgorithm;
48         setStackScroll(getStackScroll());
49     }
50 
51     /** Resets the task scroller. */
reset()52     void reset() {
53         mStackScrollP = 0f;
54     }
55 
56     /** Sets the callbacks */
setCallbacks(TaskStackViewScrollerCallbacks cb)57     void setCallbacks(TaskStackViewScrollerCallbacks cb) {
58         mCb = cb;
59     }
60 
61     /** Gets the current stack scroll */
getStackScroll()62     public float getStackScroll() {
63         return mStackScrollP;
64     }
65 
66     /** Sets the current stack scroll */
setStackScroll(float s)67     public void setStackScroll(float s) {
68         mStackScrollP = s;
69         if (mCb != null) {
70             mCb.onScrollChanged(mStackScrollP);
71         }
72     }
73 
74     /** Sets the current stack scroll without calling the callback. */
setStackScrollRaw(float s)75     void setStackScrollRaw(float s) {
76         mStackScrollP = s;
77     }
78 
79     /**
80      * Sets the current stack scroll to the initial state when you first enter recents.
81      * @return whether the stack progress changed.
82      */
setStackScrollToInitialState()83     public boolean setStackScrollToInitialState() {
84         float prevStackScrollP = mStackScrollP;
85         setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
86         return Float.compare(prevStackScrollP, mStackScrollP) != 0;
87     }
88 
89     /** Bounds the current scroll if necessary */
boundScroll()90     public boolean boundScroll() {
91         float curScroll = getStackScroll();
92         float newScroll = getBoundedStackScroll(curScroll);
93         if (Float.compare(newScroll, curScroll) != 0) {
94             setStackScroll(newScroll);
95             return true;
96         }
97         return false;
98     }
99 
100     /** Returns the bounded stack scroll */
getBoundedStackScroll(float scroll)101     float getBoundedStackScroll(float scroll) {
102         return Math.max(mLayoutAlgorithm.mMinScrollP, Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
103     }
104 
105     /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
getScrollAmountOutOfBounds(float scroll)106     float getScrollAmountOutOfBounds(float scroll) {
107         if (scroll < mLayoutAlgorithm.mMinScrollP) {
108             return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
109         } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
110             return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
111         }
112         return 0f;
113     }
114 
115     /** Returns whether the specified scroll is out of bounds */
isScrollOutOfBounds()116     boolean isScrollOutOfBounds() {
117         return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
118     }
119 
120     /** Animates the stack scroll into bounds */
animateBoundScroll()121     ObjectAnimator animateBoundScroll() {
122         float curScroll = getStackScroll();
123         float newScroll = getBoundedStackScroll(curScroll);
124         if (Float.compare(newScroll, curScroll) != 0) {
125             // Start a new scroll animation
126             animateScroll(curScroll, newScroll, null);
127         }
128         return mScrollAnimator;
129     }
130 
131     /** Animates the stack scroll */
animateScroll(float curScroll, float newScroll, final Runnable postRunnable)132     void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
133         // Finish any current scrolling animations
134         if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
135             setStackScroll(mFinalAnimatedScroll);
136             mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
137         }
138         stopScroller();
139         stopBoundScrollAnimation();
140 
141         mFinalAnimatedScroll = newScroll;
142         mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
143         mScrollAnimator.setDuration(mConfig.taskStackScrollDuration);
144         mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator);
145         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
146             @Override
147             public void onAnimationUpdate(ValueAnimator animation) {
148                 setStackScroll((Float) animation.getAnimatedValue());
149             }
150         });
151         mScrollAnimator.addListener(new AnimatorListenerAdapter() {
152             @Override
153             public void onAnimationEnd(Animator animation) {
154                 if (postRunnable != null) {
155                     postRunnable.run();
156                 }
157                 mScrollAnimator.removeAllListeners();
158             }
159         });
160         mScrollAnimator.start();
161     }
162 
163     /** Aborts any current stack scrolls */
stopBoundScrollAnimation()164     void stopBoundScrollAnimation() {
165         Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
166     }
167 
168     /**** OverScroller ****/
169 
progressToScrollRange(float p)170     int progressToScrollRange(float p) {
171         return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
172     }
173 
scrollRangeToProgress(int s)174     float scrollRangeToProgress(int s) {
175         return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
176     }
177 
178     /** Called from the view draw, computes the next scroll. */
computeScroll()179     boolean computeScroll() {
180         if (mScroller.computeScrollOffset()) {
181             float scroll = scrollRangeToProgress(mScroller.getCurrY());
182             setStackScrollRaw(scroll);
183             if (mCb != null) {
184                 mCb.onScrollChanged(scroll);
185             }
186             return true;
187         }
188         return false;
189     }
190 
191     /** Returns whether the overscroller is scrolling. */
isScrolling()192     boolean isScrolling() {
193         return !mScroller.isFinished();
194     }
195 
196     /** Stops the scroller and any current fling. */
stopScroller()197     void stopScroller() {
198         if (!mScroller.isFinished()) {
199             mScroller.abortAnimation();
200         }
201     }
202 }