TaskStackViewScroller.java revision 116b2c2c5430122ad7dfa14470bc640bf3c1c59f
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.content.Context;
23import android.util.Log;
24import android.view.animation.AnimationUtils;
25import android.view.animation.Interpolator;
26import android.widget.OverScroller;
27import com.android.systemui.R;
28import com.android.systemui.recents.misc.Utilities;
29
30/* The scrolling logic for a TaskStackView */
31public class TaskStackViewScroller {
32
33    private static final String TAG = "TaskStackViewScroller";
34    private static final boolean DEBUG = false;
35
36    public interface TaskStackViewScrollerCallbacks {
37        void onScrollChanged(float prevScroll, float curScroll);
38    }
39
40    Context mContext;
41    TaskStackLayoutAlgorithm mLayoutAlgorithm;
42    TaskStackViewScrollerCallbacks mCb;
43
44    float mStackScrollP;
45    float mFlingDownScrollP;
46    int mFlingDownY;
47
48    OverScroller mScroller;
49    ObjectAnimator mScrollAnimator;
50    float mFinalAnimatedScroll;
51
52    private Interpolator mLinearOutSlowInInterpolator;
53
54    public TaskStackViewScroller(Context context, TaskStackLayoutAlgorithm layoutAlgorithm) {
55        mContext = context;
56        mScroller = new OverScroller(context);
57        mLayoutAlgorithm = layoutAlgorithm;
58        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
59                com.android.internal.R.interpolator.linear_out_slow_in);
60        setStackScroll(getStackScroll());
61    }
62
63    /** Resets the task scroller. */
64    void reset() {
65        mStackScrollP = 0f;
66    }
67
68    /** Sets the callbacks */
69    void setCallbacks(TaskStackViewScrollerCallbacks cb) {
70        mCb = cb;
71    }
72
73    /** Gets the current stack scroll */
74    public float getStackScroll() {
75        return mStackScrollP;
76    }
77
78    /** Sets the current stack scroll */
79    public void setStackScroll(float s) {
80        float prevStackScroll = mStackScrollP;
81        mStackScrollP = s;
82        if (mCb != null) {
83            mCb.onScrollChanged(prevStackScroll, mStackScrollP);
84        }
85    }
86
87    /**
88     * Sets the current stack scroll to the initial state when you first enter recents.
89     * @return whether the stack progress changed.
90     */
91    public boolean setStackScrollToInitialState() {
92        float prevStackScrollP = mStackScrollP;
93        setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP));
94        return Float.compare(prevStackScrollP, mStackScrollP) != 0;
95    }
96
97    /**
98     * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
99     */
100    public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
101            int overscroll) {
102        if (DEBUG) {
103            Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
104                    ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
105        }
106        mFlingDownScrollP = downScrollP;
107        mFlingDownY = downY;
108        mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
109    }
110
111    /** Bounds the current scroll if necessary */
112    public boolean boundScroll() {
113        float curScroll = getStackScroll();
114        float newScroll = getBoundedStackScroll(curScroll);
115        if (Float.compare(newScroll, curScroll) != 0) {
116            setStackScroll(newScroll);
117            return true;
118        }
119        return false;
120    }
121
122    /** Returns the bounded stack scroll */
123    float getBoundedStackScroll(float scroll) {
124        return Math.max(mLayoutAlgorithm.mMinScrollP,
125                Math.min(mLayoutAlgorithm.mMaxScrollP, scroll));
126    }
127
128    /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
129    float getScrollAmountOutOfBounds(float scroll) {
130        if (scroll < mLayoutAlgorithm.mMinScrollP) {
131            return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
132        } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
133            return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
134        }
135        return 0f;
136    }
137
138    /** Returns whether the specified scroll is out of bounds */
139    boolean isScrollOutOfBounds() {
140        return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
141    }
142
143    /** Animates the stack scroll into bounds */
144    ObjectAnimator animateBoundScroll() {
145        float curScroll = getStackScroll();
146        float newScroll = getBoundedStackScroll(curScroll);
147        if (Float.compare(newScroll, curScroll) != 0) {
148            // Start a new scroll animation
149            animateScroll(curScroll, newScroll, null);
150        }
151        return mScrollAnimator;
152    }
153
154    /** Animates the stack scroll */
155    void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) {
156        // Finish any current scrolling animations
157        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
158            setStackScroll(mFinalAnimatedScroll);
159            mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0);
160        }
161        stopScroller();
162        stopBoundScrollAnimation();
163
164        mFinalAnimatedScroll = newScroll;
165        mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll);
166        mScrollAnimator.setDuration(mContext.getResources().getInteger(
167                R.integer.recents_animate_task_stack_scroll_duration));
168        mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator);
169        mScrollAnimator.addListener(new AnimatorListenerAdapter() {
170            @Override
171            public void onAnimationEnd(Animator animation) {
172                if (postRunnable != null) {
173                    postRunnable.run();
174                }
175                mScrollAnimator.removeAllListeners();
176            }
177        });
178        mScrollAnimator.start();
179    }
180
181    /** Aborts any current stack scrolls */
182    void stopBoundScrollAnimation() {
183        Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
184    }
185
186    /**** OverScroller ****/
187
188    // TODO: Remove
189    @Deprecated
190    int progressToScrollRange(float p) {
191        return (int) (p * mLayoutAlgorithm.mStackRect.height());
192    }
193
194    /** Called from the view draw, computes the next scroll. */
195    boolean computeScroll() {
196        if (mScroller.computeScrollOffset()) {
197            float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
198            float scroll = mFlingDownScrollP + deltaP;
199            setStackScroll(scroll);
200            if (DEBUG) {
201                Log.d(TAG, "computeScroll: " + scroll);
202            }
203            return true;
204        }
205        return false;
206    }
207
208    /** Returns whether the overscroller is scrolling. */
209    boolean isScrolling() {
210        return !mScroller.isFinished();
211    }
212
213    /** Stops the scroller and any current fling. */
214    void stopScroller() {
215        if (!mScroller.isFinished()) {
216            mScroller.abortAnimation();
217        }
218    }
219}