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