blob: c8c71cb441b8ad1db25a6959cd2760a9c9b343e1 [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;
44
45 private final ArrayList<View> mAllViews = new ArrayList<>();
Jason Monke80654b2016-02-24 14:53:57 -050046 private final ArrayList<View> mTopFiveQs = new ArrayList<>();
Jason Monk162011e2016-02-19 08:11:55 -050047 private final QuickQSPanel mQuickQsPanel;
48 private final QSPanel mQsPanel;
49 private final QSContainer mQsContainer;
50
Jason Monk8fb77872016-03-03 16:39:42 -050051 private PagedTileLayout mPagedLayout;
52
Jason Monk162011e2016-02-19 08:11:55 -050053 private boolean mOnFirstPage = true;
54 private TouchAnimator mFirstPageAnimator;
55 private TouchAnimator mFirstPageDelayedAnimator;
Jason Monkfa0f47a2016-03-08 09:17:56 -050056 private TouchAnimator mTranslationAnimator;
Jason Monk162011e2016-02-19 08:11:55 -050057 private TouchAnimator mNonfirstPageAnimator;
58
Jason Monk8fb77872016-03-03 16:39:42 -050059 private boolean mOnKeyguard;
60
61 private boolean mAllowFancy;
62 private boolean mFullRows;
63 private int mNumQuickTiles;
64
Jason Monk162011e2016-02-19 08:11:55 -050065 public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
66 mQsContainer = container;
67 mQuickQsPanel = quickPanel;
68 mQsPanel = panel;
Jason Monk8fb77872016-03-03 16:39:42 -050069 mQsPanel.addOnAttachStateChangeListener(this);
Jason Monkdf5459d2016-02-29 10:47:35 -050070 container.addOnLayoutChangeListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050071 QSTileLayout tileLayout = mQsPanel.getTileLayout();
72 if (tileLayout instanceof PagedTileLayout) {
Jason Monk8fb77872016-03-03 16:39:42 -050073 mPagedLayout = ((PagedTileLayout) tileLayout);
74 mPagedLayout.setPageListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050075 } else {
76 Log.w(TAG, "QS Not using page layout");
77 }
78 }
79
Jason Monk8fb77872016-03-03 16:39:42 -050080 public void setOnKeyguard(boolean onKeyguard) {
81 mOnKeyguard = onKeyguard;
82 if (mOnKeyguard) {
83 clearAnimationState();
84 }
85 }
86
Jason Monk162011e2016-02-19 08:11:55 -050087 public void setHost(QSTileHost qsh) {
88 qsh.addCallback(this);
89 }
90
91 @Override
Jason Monk8fb77872016-03-03 16:39:42 -050092 public void onViewAttachedToWindow(View v) {
93 TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
94 MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
95 }
96
97 @Override
98 public void onViewDetachedFromWindow(View v) {
99 TunerService.get(mQsContainer.getContext()).removeTunable(this);
100 }
101
102 @Override
103 public void onTuningChanged(String key, String newValue) {
104 if (ALLOW_FANCY_ANIMATION.equals(key)) {
105 mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
106 if (!mAllowFancy) {
107 clearAnimationState();
108 }
109 } else if (MOVE_FULL_ROWS.equals(key)) {
Jason Monk39a2e702016-03-08 16:41:23 -0500110 mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
Jason Monk8fb77872016-03-03 16:39:42 -0500111 } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
112 mNumQuickTiles = QuickQSPanel.getNumQuickTiles(mQsContainer.getContext());
113 clearAnimationState();
114 }
115 updateAnimators();
116 }
117
118 @Override
Jason Monk66eaf312016-02-25 12:29:29 -0500119 public void onPageChanged(boolean isFirst) {
120 if (mOnFirstPage == isFirst) return;
121 if (!isFirst) {
Jason Monk162011e2016-02-19 08:11:55 -0500122 clearAnimationState();
123 }
Jason Monk66eaf312016-02-25 12:29:29 -0500124 mOnFirstPage = isFirst;
Jason Monk162011e2016-02-19 08:11:55 -0500125 }
126
127 private void updateAnimators() {
128 TouchAnimator.Builder firstPageBuilder = new Builder();
Jason Monkfa0f47a2016-03-08 09:17:56 -0500129 TouchAnimator.Builder translationXBuilder = new Builder();
Jason Monk162011e2016-02-19 08:11:55 -0500130 TouchAnimator.Builder translationYBuilder = new Builder();
131 TouchAnimator.Builder firstPageDelayedBuilder = new Builder();
132 Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
133 int count = 0;
134 int[] loc1 = new int[2];
135 int[] loc2 = new int[2];
Jason Monk8fb77872016-03-03 16:39:42 -0500136 int lastYDiff = 0;
Jason Monk162011e2016-02-19 08:11:55 -0500137 firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
138 firstPageBuilder.setListener(this);
Jason Monke80654b2016-02-24 14:53:57 -0500139 // Fade in the tiles/labels as we reach the final position.
140 firstPageDelayedBuilder.addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1);
Jason Monk162011e2016-02-19 08:11:55 -0500141 mAllViews.clear();
Jason Monke80654b2016-02-24 14:53:57 -0500142 mTopFiveQs.clear();
Jason Monk8fb77872016-03-03 16:39:42 -0500143 mAllViews.add((View) mQsPanel.getTileLayout());
Jason Monk162011e2016-02-19 08:11:55 -0500144 for (QSTile<?> tile : tiles) {
145 QSTileBaseView tileView = mQsPanel.getTileView(tile);
146 final TextView label = ((QSTileView) tileView).getLabel();
Jason Monk8fb77872016-03-03 16:39:42 -0500147 final View tileIcon = tileView.getIcon();
148 if (count < mNumQuickTiles && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500149 // Quick tiles.
150 QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
Jason Monk162011e2016-02-19 08:11:55 -0500151
152 getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
153 getRelativePosition(loc2, tileIcon, mQsContainer);
154 final int xDiff = loc2[0] - loc1[0];
155 final int yDiff = loc2[1] - loc1[1];
Jason Monk8fb77872016-03-03 16:39:42 -0500156 lastYDiff = yDiff;
Jason Monk162011e2016-02-19 08:11:55 -0500157 // Move the quick tile right from its location to the new one.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500158 translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
Jason Monk162011e2016-02-19 08:11:55 -0500159 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
160
161 // Counteract the parent translation on the tile. So we have a static base to
Jason Monke80654b2016-02-24 14:53:57 -0500162 // animate the label position off from.
Jason Monk162011e2016-02-19 08:11:55 -0500163 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
164
Jason Monke80654b2016-02-24 14:53:57 -0500165 // Move the real tile's label from the quick tile position to its final
Jason Monk162011e2016-02-19 08:11:55 -0500166 // location.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500167 translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
Jason Monk162011e2016-02-19 08:11:55 -0500168 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
169
Jason Monke80654b2016-02-24 14:53:57 -0500170 mTopFiveQs.add(tileIcon);
171 mAllViews.add(tileIcon);
Jason Monk8fb77872016-03-03 16:39:42 -0500172 mAllViews.add(label);
Jason Monk162011e2016-02-19 08:11:55 -0500173 mAllViews.add(quickTileView);
Jason Monk8fb77872016-03-03 16:39:42 -0500174 } else if (mFullRows && isIconInAnimatedRow(count)) {
175 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
176 translationYBuilder.addFloat(label, "translationY", -lastYDiff, 0);
177 translationYBuilder.addFloat(tileIcon, "translationY", -lastYDiff, 0);
178 mAllViews.add(tileIcon);
179 mAllViews.add(label);
Jason Monk162011e2016-02-19 08:11:55 -0500180 }
181 mAllViews.add(tileView);
182 mAllViews.add(label);
Jason Monk8fb77872016-03-03 16:39:42 -0500183 count++;
Jason Monk162011e2016-02-19 08:11:55 -0500184 }
Jason Monk8fb77872016-03-03 16:39:42 -0500185 if (mAllowFancy) {
186 mFirstPageAnimator = firstPageBuilder.build();
187 mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
Jason Monkfa0f47a2016-03-08 09:17:56 -0500188 Path path = new Path();
189 path.moveTo(0, 0);
190 path.cubicTo(0, 0, 0, 1, 1, 1);
191 mTranslationAnimator = new TouchAnimator.Builder()
192 .addPath(translationXBuilder.build(), translationYBuilder.build(),
193 "position", "position", path)
194 .build();
Jason Monk8fb77872016-03-03 16:39:42 -0500195 }
Jason Monk162011e2016-02-19 08:11:55 -0500196 mNonfirstPageAnimator = new TouchAnimator.Builder()
197 .addFloat(mQuickQsPanel, "alpha", 1, 0)
198 .setEndDelay(.5f)
199 .build();
200 }
201
Jason Monk8fb77872016-03-03 16:39:42 -0500202 private boolean isIconInAnimatedRow(int count) {
203 if (mPagedLayout == null) {
204 return false;
205 }
206 final int columnCount = mPagedLayout.getColumnCount();
207 return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
208 }
209
Jason Monk162011e2016-02-19 08:11:55 -0500210 private void getRelativePosition(int[] loc1, View view, View parent) {
211 loc1[0] = 0 + view.getWidth() / 2;
212 loc1[1] = 0;
213 getRelativePositionInt(loc1, view, parent);
214 }
215
216 private void getRelativePositionInt(int[] loc1, View view, View parent) {
217 if(view == parent || view == null) return;
218 loc1[0] += view.getLeft();
219 loc1[1] += view.getTop();
220 getRelativePositionInt(loc1, (View) view.getParent(), parent);
221 }
222
223 public void setPosition(float position) {
224 if (mFirstPageAnimator == null) return;
Jason Monk8fb77872016-03-03 16:39:42 -0500225 if (mOnKeyguard) {
226 return;
227 }
228 if (mOnFirstPage && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500229 mQuickQsPanel.setAlpha(1);
230 mFirstPageAnimator.setPosition(position);
231 mFirstPageDelayedAnimator.setPosition(position);
Jason Monkfa0f47a2016-03-08 09:17:56 -0500232 mTranslationAnimator.setPosition(position);
Jason Monk162011e2016-02-19 08:11:55 -0500233 } else {
234 mNonfirstPageAnimator.setPosition(position);
235 }
236 }
237
238 @Override
239 public void onAnimationAtStart() {
Jason Monkdf5459d2016-02-29 10:47:35 -0500240 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monk162011e2016-02-19 08:11:55 -0500241 }
242
243 @Override
244 public void onAnimationAtEnd() {
245 mQuickQsPanel.setVisibility(View.INVISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500246 final int N = mTopFiveQs.size();
247 for (int i = 0; i < N; i++) {
248 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
249 }
Jason Monk162011e2016-02-19 08:11:55 -0500250 }
251
252 @Override
253 public void onAnimationStarted() {
254 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500255 if (mOnFirstPage) {
256 final int N = mTopFiveQs.size();
257 for (int i = 0; i < N; i++) {
258 mTopFiveQs.get(i).setVisibility(View.INVISIBLE);
259 }
260 }
Jason Monk162011e2016-02-19 08:11:55 -0500261 }
262
263 private void clearAnimationState() {
264 final int N = mAllViews.size();
265 mQuickQsPanel.setAlpha(0);
Jason Monk8fb77872016-03-03 16:39:42 -0500266 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monk162011e2016-02-19 08:11:55 -0500267 for (int i = 0; i < N; i++) {
268 View v = mAllViews.get(i);
269 v.setAlpha(1);
270 v.setTranslationX(1);
271 v.setTranslationY(1);
272 }
Jason Monk8fb77872016-03-03 16:39:42 -0500273 final int N2 = mTopFiveQs.size();
274 for (int i = 0; i < N2; i++) {
275 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
276 }
Jason Monk162011e2016-02-19 08:11:55 -0500277 }
278
279 @Override
280 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
281 int oldTop, int oldRight, int oldBottom) {
282 updateAnimators();
283 }
284
285 @Override
286 public void onTilesChanged() {
287 // Give the QS panels a moment to generate their new tiles, then create all new animators
288 // hooked up to the new views.
289 mQsPanel.post(mUpdateAnimators);
290 }
291
292 private Runnable mUpdateAnimators = new Runnable() {
293 @Override
294 public void run() {
295 updateAnimators();
296 }
297 };
298}