blob: 0a12dab0821ba0fa5f7eff463eb10bfa89c6cca5 [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;
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(250);
137 // We would have to project the difference into the screen coords, and then use that as the
138 // duration
139// mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
140// curScroll, 250));
141 mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
142 mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
143 @Override
144 public void onAnimationUpdate(ValueAnimator animation) {
145 setStackScroll((Float) animation.getAnimatedValue());
146 }
147 });
148 mScrollAnimator.addListener(new AnimatorListenerAdapter() {
149 @Override
150 public void onAnimationEnd(Animator animation) {
151 if (postRunnable != null) {
152 postRunnable.run();
153 }
154 mScrollAnimator.removeAllListeners();
155 }
156 });
157 mScrollAnimator.start();
158 }
159
160 /** Aborts any current stack scrolls */
161 void stopBoundScrollAnimation() {
162 if (mScrollAnimator != null) {
163 mScrollAnimator.removeAllListeners();
164 mScrollAnimator.cancel();
165 }
166 }
167
168 /**** OverScroller ****/
169
170 int progressToScrollRange(float p) {
171 return (int) (p * mLayoutAlgorithm.mStackVisibleRect.height());
172 }
173
174 float scrollRangeToProgress(int s) {
175 return (float) s / mLayoutAlgorithm.mStackVisibleRect.height();
176 }
177
178 /** Called from the view draw, computes the next scroll. */
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. */
192 boolean isScrolling() {
193 return !mScroller.isFinished();
194 }
195
196 /** Stops the scroller and any current fling. */
197 void stopScroller() {
198 if (!mScroller.isFinished()) {
199 mScroller.abortAnimation();
200 }
201 }
202}