blob: de8eec4cb53b924ff3069a755b4963a4ef237c69 [file] [log] [blame]
Jason Monk162011e2016-02-19 08:11:55 -05001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.qs;
16
Jason Monkfa0f47a2016-03-08 09:17:56 -050017import android.graphics.Path;
Jason Monk162011e2016-02-19 08:11:55 -050018import android.util.Log;
19import android.view.View;
Jason Monk8fb77872016-03-03 16:39:42 -050020import android.view.View.OnAttachStateChangeListener;
Jason Monk162011e2016-02-19 08:11:55 -050021import android.view.View.OnLayoutChangeListener;
Jason Monk162011e2016-02-19 08:11:55 -050022import android.widget.TextView;
Jason Monk162011e2016-02-19 08:11:55 -050023import com.android.systemui.qs.PagedTileLayout.PageListener;
24import com.android.systemui.qs.QSPanel.QSTileLayout;
25import com.android.systemui.qs.QSTile.Host.Callback;
26import com.android.systemui.qs.TouchAnimator.Builder;
27import com.android.systemui.qs.TouchAnimator.Listener;
28import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk8fb77872016-03-03 16:39:42 -050029import com.android.systemui.tuner.TunerService;
30import com.android.systemui.tuner.TunerService.Tunable;
Jason Monk162011e2016-02-19 08:11:55 -050031
32import java.util.ArrayList;
33import java.util.Collection;
34
Jason Monk8fb77872016-03-03 16:39:42 -050035public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
36 OnAttachStateChangeListener, Tunable {
Jason Monk162011e2016-02-19 08:11:55 -050037
38 private static final String TAG = "QSAnimator";
39
Jason Monk8fb77872016-03-03 16:39:42 -050040 private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
41 private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
42
Jason Monk162011e2016-02-19 08:11:55 -050043 public static final float EXPANDED_TILE_DELAY = .7f;
Jason Monk789e9c02016-03-28 16:00:09 -040044 private static final float LAST_ROW_EXPANDED_DELAY = .86f;
Jason Monk162011e2016-02-19 08:11:55 -050045
46 private final ArrayList<View> mAllViews = new ArrayList<>();
Jason Monke80654b2016-02-24 14:53:57 -050047 private final ArrayList<View> mTopFiveQs = new ArrayList<>();
Jason Monk162011e2016-02-19 08:11:55 -050048 private final QuickQSPanel mQuickQsPanel;
49 private final QSPanel mQsPanel;
50 private final QSContainer mQsContainer;
51
Jason Monk8fb77872016-03-03 16:39:42 -050052 private PagedTileLayout mPagedLayout;
53
Jason Monk162011e2016-02-19 08:11:55 -050054 private boolean mOnFirstPage = true;
55 private TouchAnimator mFirstPageAnimator;
56 private TouchAnimator mFirstPageDelayedAnimator;
Jason Monka97776a2016-03-09 15:13:02 -050057 private TouchAnimator mTranslationXAnimator;
58 private TouchAnimator mTranslationYAnimator;
Jason Monk162011e2016-02-19 08:11:55 -050059 private TouchAnimator mNonfirstPageAnimator;
Jason Monk98196b02016-03-14 10:33:02 -040060 private TouchAnimator mLastRowAnimator;
Jason Monk162011e2016-02-19 08:11:55 -050061
Jason Monk8fb77872016-03-03 16:39:42 -050062 private boolean mOnKeyguard;
63
64 private boolean mAllowFancy;
65 private boolean mFullRows;
66 private int mNumQuickTiles;
Jason Monkbe8612d2016-03-30 16:19:06 -040067 private float mLastPosition;
Jason Monk8fb77872016-03-03 16:39:42 -050068
Jason Monk162011e2016-02-19 08:11:55 -050069 public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
70 mQsContainer = container;
71 mQuickQsPanel = quickPanel;
72 mQsPanel = panel;
Jason Monk8fb77872016-03-03 16:39:42 -050073 mQsPanel.addOnAttachStateChangeListener(this);
Jason Monkdf5459d2016-02-29 10:47:35 -050074 container.addOnLayoutChangeListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050075 QSTileLayout tileLayout = mQsPanel.getTileLayout();
76 if (tileLayout instanceof PagedTileLayout) {
Jason Monk8fb77872016-03-03 16:39:42 -050077 mPagedLayout = ((PagedTileLayout) tileLayout);
78 mPagedLayout.setPageListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050079 } else {
80 Log.w(TAG, "QS Not using page layout");
81 }
82 }
83
Jason Monk51fb85a2016-03-28 14:06:04 -040084 public void onRtlChanged() {
85 updateAnimators();
86 }
87
Jason Monk8fb77872016-03-03 16:39:42 -050088 public void setOnKeyguard(boolean onKeyguard) {
89 mOnKeyguard = onKeyguard;
90 if (mOnKeyguard) {
91 clearAnimationState();
92 }
93 }
94
Jason Monk162011e2016-02-19 08:11:55 -050095 public void setHost(QSTileHost qsh) {
96 qsh.addCallback(this);
Jason Monka9df9992016-03-15 12:41:13 -040097 updateAnimators();
Jason Monk162011e2016-02-19 08:11:55 -050098 }
99
100 @Override
Jason Monk8fb77872016-03-03 16:39:42 -0500101 public void onViewAttachedToWindow(View v) {
102 TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
103 MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
104 }
105
106 @Override
107 public void onViewDetachedFromWindow(View v) {
108 TunerService.get(mQsContainer.getContext()).removeTunable(this);
109 }
110
111 @Override
112 public void onTuningChanged(String key, String newValue) {
113 if (ALLOW_FANCY_ANIMATION.equals(key)) {
114 mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
115 if (!mAllowFancy) {
116 clearAnimationState();
117 }
118 } else if (MOVE_FULL_ROWS.equals(key)) {
Jason Monk39a2e702016-03-08 16:41:23 -0500119 mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
Jason Monk8fb77872016-03-03 16:39:42 -0500120 } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
Xiaohui Chen311b98e2016-03-30 11:55:54 -0700121 mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQsContainer.getContext());
Jason Monk8fb77872016-03-03 16:39:42 -0500122 clearAnimationState();
123 }
124 updateAnimators();
125 }
126
127 @Override
Jason Monk66eaf312016-02-25 12:29:29 -0500128 public void onPageChanged(boolean isFirst) {
129 if (mOnFirstPage == isFirst) return;
130 if (!isFirst) {
Jason Monk162011e2016-02-19 08:11:55 -0500131 clearAnimationState();
132 }
Jason Monk66eaf312016-02-25 12:29:29 -0500133 mOnFirstPage = isFirst;
Jason Monk162011e2016-02-19 08:11:55 -0500134 }
135
136 private void updateAnimators() {
137 TouchAnimator.Builder firstPageBuilder = new Builder();
Jason Monkfa0f47a2016-03-08 09:17:56 -0500138 TouchAnimator.Builder translationXBuilder = new Builder();
Jason Monk162011e2016-02-19 08:11:55 -0500139 TouchAnimator.Builder translationYBuilder = new Builder();
Jason Monk98196b02016-03-14 10:33:02 -0400140 TouchAnimator.Builder lastRowBuilder = new Builder();
141
Jason Monka9df9992016-03-15 12:41:13 -0400142 if (mQsPanel.getHost() == null) return;
Jason Monk162011e2016-02-19 08:11:55 -0500143 Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
144 int count = 0;
145 int[] loc1 = new int[2];
146 int[] loc2 = new int[2];
Jason Monk789e9c02016-03-28 16:00:09 -0400147 int lastXDiff = 0;
Jason Monk8fb77872016-03-03 16:39:42 -0500148 int lastYDiff = 0;
Jason Monk789e9c02016-03-28 16:00:09 -0400149 int lastX = 0;
Jason Monk98196b02016-03-14 10:33:02 -0400150
Jason Monkbc8ffc22016-03-09 10:58:49 -0500151 clearAnimationState();
Jason Monk162011e2016-02-19 08:11:55 -0500152 mAllViews.clear();
Jason Monke80654b2016-02-24 14:53:57 -0500153 mTopFiveQs.clear();
Jason Monk98196b02016-03-14 10:33:02 -0400154
Jason Monk8fb77872016-03-03 16:39:42 -0500155 mAllViews.add((View) mQsPanel.getTileLayout());
Jason Monk98196b02016-03-14 10:33:02 -0400156
Jason Monk162011e2016-02-19 08:11:55 -0500157 for (QSTile<?> tile : tiles) {
158 QSTileBaseView tileView = mQsPanel.getTileView(tile);
159 final TextView label = ((QSTileView) tileView).getLabel();
Jason Monk8fb77872016-03-03 16:39:42 -0500160 final View tileIcon = tileView.getIcon();
161 if (count < mNumQuickTiles && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500162 // Quick tiles.
163 QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
Jason Monk162011e2016-02-19 08:11:55 -0500164
Jason Monk789e9c02016-03-28 16:00:09 -0400165 lastX = loc1[0];
Jason Monk162011e2016-02-19 08:11:55 -0500166 getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
167 getRelativePosition(loc2, tileIcon, mQsContainer);
168 final int xDiff = loc2[0] - loc1[0];
169 final int yDiff = loc2[1] - loc1[1];
Jason Monk789e9c02016-03-28 16:00:09 -0400170 lastXDiff = loc1[0] - lastX;
Jason Monk8fb77872016-03-03 16:39:42 -0500171 lastYDiff = yDiff;
Jason Monk162011e2016-02-19 08:11:55 -0500172 // Move the quick tile right from its location to the new one.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500173 translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
Jason Monk162011e2016-02-19 08:11:55 -0500174 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
175
176 // Counteract the parent translation on the tile. So we have a static base to
Jason Monke80654b2016-02-24 14:53:57 -0500177 // animate the label position off from.
Jason Monk162011e2016-02-19 08:11:55 -0500178 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
179
Jason Monke80654b2016-02-24 14:53:57 -0500180 // Move the real tile's label from the quick tile position to its final
Jason Monk162011e2016-02-19 08:11:55 -0500181 // location.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500182 translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
Jason Monk162011e2016-02-19 08:11:55 -0500183 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
184
Jason Monke80654b2016-02-24 14:53:57 -0500185 mTopFiveQs.add(tileIcon);
186 mAllViews.add(tileIcon);
Jason Monk162011e2016-02-19 08:11:55 -0500187 mAllViews.add(quickTileView);
Jason Monk8fb77872016-03-03 16:39:42 -0500188 } else if (mFullRows && isIconInAnimatedRow(count)) {
Jason Monk789e9c02016-03-28 16:00:09 -0400189 // TODO: Refactor some of this, it shares a lot with the above block.
190 // Move the last tile position over by the last difference between quick tiles.
191 // This makes the extra icons seems as if they are coming from positions in the
192 // quick panel.
193 loc1[0] += lastXDiff;
194 getRelativePosition(loc2, tileIcon, mQsContainer);
195 final int xDiff = loc2[0] - loc1[0];
196 final int yDiff = loc2[1] - loc1[1];
197
Jason Monk8fb77872016-03-03 16:39:42 -0500198 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
Jason Monk789e9c02016-03-28 16:00:09 -0400199 translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
200 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
201 translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
202
Jason Monk8fb77872016-03-03 16:39:42 -0500203 mAllViews.add(tileIcon);
Jason Monk98196b02016-03-14 10:33:02 -0400204 } else {
205 lastRowBuilder.addFloat(tileView, "alpha", 0, 1);
Jason Monk162011e2016-02-19 08:11:55 -0500206 }
207 mAllViews.add(tileView);
208 mAllViews.add(label);
Jason Monk8fb77872016-03-03 16:39:42 -0500209 count++;
Jason Monk162011e2016-02-19 08:11:55 -0500210 }
Jason Monk8fb77872016-03-03 16:39:42 -0500211 if (mAllowFancy) {
Jason Monk98196b02016-03-14 10:33:02 -0400212 mFirstPageAnimator = firstPageBuilder
213 .setListener(this)
214 .build();
215 // Fade in the tiles/labels as we reach the final position.
216 mFirstPageDelayedAnimator = new TouchAnimator.Builder()
217 .setStartDelay(EXPANDED_TILE_DELAY)
218 .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1).build();
219 mLastRowAnimator = lastRowBuilder
220 .setStartDelay(LAST_ROW_EXPANDED_DELAY)
221 .build();
Jason Monkfa0f47a2016-03-08 09:17:56 -0500222 Path path = new Path();
223 path.moveTo(0, 0);
224 path.cubicTo(0, 0, 0, 1, 1, 1);
Jason Monka97776a2016-03-09 15:13:02 -0500225 PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, 0, 1);
226 translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
227 translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
228 mTranslationXAnimator = translationXBuilder.build();
229 mTranslationYAnimator = translationYBuilder.build();
Jason Monk8fb77872016-03-03 16:39:42 -0500230 }
Jason Monk162011e2016-02-19 08:11:55 -0500231 mNonfirstPageAnimator = new TouchAnimator.Builder()
232 .addFloat(mQuickQsPanel, "alpha", 1, 0)
Jason Monk79a9f2f2016-03-10 14:55:22 -0500233 .setListener(mNonFirstPageListener)
Jason Monk162011e2016-02-19 08:11:55 -0500234 .setEndDelay(.5f)
235 .build();
236 }
237
Jason Monk8fb77872016-03-03 16:39:42 -0500238 private boolean isIconInAnimatedRow(int count) {
239 if (mPagedLayout == null) {
240 return false;
241 }
242 final int columnCount = mPagedLayout.getColumnCount();
243 return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
244 }
245
Jason Monk162011e2016-02-19 08:11:55 -0500246 private void getRelativePosition(int[] loc1, View view, View parent) {
247 loc1[0] = 0 + view.getWidth() / 2;
248 loc1[1] = 0;
249 getRelativePositionInt(loc1, view, parent);
250 }
251
252 private void getRelativePositionInt(int[] loc1, View view, View parent) {
253 if(view == parent || view == null) return;
Jason Monk51fb85a2016-03-28 14:06:04 -0400254 loc1[0] += view.getX();
Jason Monk162011e2016-02-19 08:11:55 -0500255 loc1[1] += view.getTop();
256 getRelativePositionInt(loc1, (View) view.getParent(), parent);
257 }
258
259 public void setPosition(float position) {
260 if (mFirstPageAnimator == null) return;
Jason Monk8fb77872016-03-03 16:39:42 -0500261 if (mOnKeyguard) {
262 return;
263 }
Jason Monkbe8612d2016-03-30 16:19:06 -0400264 mLastPosition = position;
Jason Monk8fb77872016-03-03 16:39:42 -0500265 if (mOnFirstPage && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500266 mQuickQsPanel.setAlpha(1);
267 mFirstPageAnimator.setPosition(position);
268 mFirstPageDelayedAnimator.setPosition(position);
Jason Monka97776a2016-03-09 15:13:02 -0500269 mTranslationXAnimator.setPosition(position);
270 mTranslationYAnimator.setPosition(position);
Jason Monk98196b02016-03-14 10:33:02 -0400271 mLastRowAnimator.setPosition(position);
Jason Monk162011e2016-02-19 08:11:55 -0500272 } else {
273 mNonfirstPageAnimator.setPosition(position);
274 }
275 }
276
277 @Override
278 public void onAnimationAtStart() {
Jason Monkdf5459d2016-02-29 10:47:35 -0500279 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monk162011e2016-02-19 08:11:55 -0500280 }
281
282 @Override
283 public void onAnimationAtEnd() {
284 mQuickQsPanel.setVisibility(View.INVISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500285 final int N = mTopFiveQs.size();
286 for (int i = 0; i < N; i++) {
287 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
288 }
Jason Monk162011e2016-02-19 08:11:55 -0500289 }
290
291 @Override
292 public void onAnimationStarted() {
293 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500294 if (mOnFirstPage) {
295 final int N = mTopFiveQs.size();
296 for (int i = 0; i < N; i++) {
297 mTopFiveQs.get(i).setVisibility(View.INVISIBLE);
298 }
299 }
Jason Monk162011e2016-02-19 08:11:55 -0500300 }
301
302 private void clearAnimationState() {
303 final int N = mAllViews.size();
304 mQuickQsPanel.setAlpha(0);
Jason Monkbe8612d2016-03-30 16:19:06 -0400305 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monk162011e2016-02-19 08:11:55 -0500306 for (int i = 0; i < N; i++) {
307 View v = mAllViews.get(i);
308 v.setAlpha(1);
309 v.setTranslationX(1);
310 v.setTranslationY(1);
311 }
Jason Monk8fb77872016-03-03 16:39:42 -0500312 final int N2 = mTopFiveQs.size();
313 for (int i = 0; i < N2; i++) {
314 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
315 }
Jason Monk162011e2016-02-19 08:11:55 -0500316 }
317
318 @Override
319 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
320 int oldTop, int oldRight, int oldBottom) {
Jason Monk51fb85a2016-03-28 14:06:04 -0400321 mQsPanel.post(mUpdateAnimators);
Jason Monk162011e2016-02-19 08:11:55 -0500322 }
323
324 @Override
325 public void onTilesChanged() {
326 // Give the QS panels a moment to generate their new tiles, then create all new animators
327 // hooked up to the new views.
328 mQsPanel.post(mUpdateAnimators);
329 }
330
Jason Monk79a9f2f2016-03-10 14:55:22 -0500331 private final TouchAnimator.Listener mNonFirstPageListener =
332 new TouchAnimator.ListenerAdapter() {
333 @Override
334 public void onAnimationStarted() {
335 mQuickQsPanel.setVisibility(View.VISIBLE);
336 }
337 };
338
Jason Monk162011e2016-02-19 08:11:55 -0500339 private Runnable mUpdateAnimators = new Runnable() {
340 @Override
341 public void run() {
342 updateAnimators();
Jason Monkbe8612d2016-03-30 16:19:06 -0400343 setPosition(mLastPosition);
Jason Monk162011e2016-02-19 08:11:55 -0500344 }
345 };
346}