blob: a172e19ff750b98ec3098f10b16787c711547416 [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 Monk9ffd9f62016-06-07 16:06:59 -040023
Jason Monk162011e2016-02-19 08:11:55 -050024import com.android.systemui.qs.PagedTileLayout.PageListener;
25import com.android.systemui.qs.QSPanel.QSTileLayout;
26import com.android.systemui.qs.QSTile.Host.Callback;
27import com.android.systemui.qs.TouchAnimator.Builder;
28import com.android.systemui.qs.TouchAnimator.Listener;
29import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk8fb77872016-03-03 16:39:42 -050030import com.android.systemui.tuner.TunerService;
31import com.android.systemui.tuner.TunerService.Tunable;
Jason Monk162011e2016-02-19 08:11:55 -050032
33import java.util.ArrayList;
34import java.util.Collection;
35
Jason Monk8fb77872016-03-03 16:39:42 -050036public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
37 OnAttachStateChangeListener, Tunable {
Jason Monk162011e2016-02-19 08:11:55 -050038
39 private static final String TAG = "QSAnimator";
40
Jason Monk8fb77872016-03-03 16:39:42 -050041 private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim";
42 private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
43
Jason Monkb46059a2016-06-30 14:22:42 -040044 public static final float EXPANDED_TILE_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 Monkb46059a2016-06-30 14:22:42 -040060 private TouchAnimator mBrightnessAnimator;
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 Monk9ffd9f62016-06-07 16:06:59 -040068 private QSTileHost mHost;
Jason Monk8fb77872016-03-03 16:39:42 -050069
Jason Monk162011e2016-02-19 08:11:55 -050070 public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
71 mQsContainer = container;
72 mQuickQsPanel = quickPanel;
73 mQsPanel = panel;
Jason Monk8fb77872016-03-03 16:39:42 -050074 mQsPanel.addOnAttachStateChangeListener(this);
Jason Monkdf5459d2016-02-29 10:47:35 -050075 container.addOnLayoutChangeListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050076 QSTileLayout tileLayout = mQsPanel.getTileLayout();
77 if (tileLayout instanceof PagedTileLayout) {
Jason Monk8fb77872016-03-03 16:39:42 -050078 mPagedLayout = ((PagedTileLayout) tileLayout);
79 mPagedLayout.setPageListener(this);
Jason Monk162011e2016-02-19 08:11:55 -050080 } else {
81 Log.w(TAG, "QS Not using page layout");
82 }
83 }
84
Jason Monk51fb85a2016-03-28 14:06:04 -040085 public void onRtlChanged() {
86 updateAnimators();
87 }
88
Jason Monk8fb77872016-03-03 16:39:42 -050089 public void setOnKeyguard(boolean onKeyguard) {
90 mOnKeyguard = onKeyguard;
Jason Monk5737ef32016-04-12 14:35:13 -040091 mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
Jason Monk8fb77872016-03-03 16:39:42 -050092 if (mOnKeyguard) {
93 clearAnimationState();
94 }
95 }
96
Jason Monk162011e2016-02-19 08:11:55 -050097 public void setHost(QSTileHost qsh) {
Jason Monk9ffd9f62016-06-07 16:06:59 -040098 mHost = qsh;
Jason Monk162011e2016-02-19 08:11:55 -050099 qsh.addCallback(this);
Jason Monka9df9992016-03-15 12:41:13 -0400100 updateAnimators();
Jason Monk162011e2016-02-19 08:11:55 -0500101 }
102
103 @Override
Jason Monk8fb77872016-03-03 16:39:42 -0500104 public void onViewAttachedToWindow(View v) {
105 TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
106 MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
107 }
108
109 @Override
110 public void onViewDetachedFromWindow(View v) {
Jason Monk9ffd9f62016-06-07 16:06:59 -0400111 if (mHost != null) {
112 mHost.removeCallback(this);
113 }
Jason Monk8fb77872016-03-03 16:39:42 -0500114 TunerService.get(mQsContainer.getContext()).removeTunable(this);
115 }
116
117 @Override
118 public void onTuningChanged(String key, String newValue) {
119 if (ALLOW_FANCY_ANIMATION.equals(key)) {
120 mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
121 if (!mAllowFancy) {
122 clearAnimationState();
123 }
124 } else if (MOVE_FULL_ROWS.equals(key)) {
Jason Monk39a2e702016-03-08 16:41:23 -0500125 mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
Jason Monk8fb77872016-03-03 16:39:42 -0500126 } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
Xiaohui Chen311b98e2016-03-30 11:55:54 -0700127 mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQsContainer.getContext());
Jason Monk8fb77872016-03-03 16:39:42 -0500128 clearAnimationState();
129 }
130 updateAnimators();
131 }
132
133 @Override
Jason Monk66eaf312016-02-25 12:29:29 -0500134 public void onPageChanged(boolean isFirst) {
135 if (mOnFirstPage == isFirst) return;
136 if (!isFirst) {
Jason Monk162011e2016-02-19 08:11:55 -0500137 clearAnimationState();
138 }
Jason Monk66eaf312016-02-25 12:29:29 -0500139 mOnFirstPage = isFirst;
Jason Monk162011e2016-02-19 08:11:55 -0500140 }
141
142 private void updateAnimators() {
143 TouchAnimator.Builder firstPageBuilder = new Builder();
Jason Monkfa0f47a2016-03-08 09:17:56 -0500144 TouchAnimator.Builder translationXBuilder = new Builder();
Jason Monk162011e2016-02-19 08:11:55 -0500145 TouchAnimator.Builder translationYBuilder = new Builder();
Jason Monk98196b02016-03-14 10:33:02 -0400146
Jason Monka9df9992016-03-15 12:41:13 -0400147 if (mQsPanel.getHost() == null) return;
Jason Monk162011e2016-02-19 08:11:55 -0500148 Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
149 int count = 0;
150 int[] loc1 = new int[2];
151 int[] loc2 = new int[2];
Jason Monk789e9c02016-03-28 16:00:09 -0400152 int lastXDiff = 0;
Jason Monk789e9c02016-03-28 16:00:09 -0400153 int lastX = 0;
Jason Monk98196b02016-03-14 10:33:02 -0400154
Jason Monkbc8ffc22016-03-09 10:58:49 -0500155 clearAnimationState();
Jason Monk162011e2016-02-19 08:11:55 -0500156 mAllViews.clear();
Jason Monke80654b2016-02-24 14:53:57 -0500157 mTopFiveQs.clear();
Jason Monk98196b02016-03-14 10:33:02 -0400158
Jason Monk8fb77872016-03-03 16:39:42 -0500159 mAllViews.add((View) mQsPanel.getTileLayout());
Jason Monk98196b02016-03-14 10:33:02 -0400160
Jason Monk162011e2016-02-19 08:11:55 -0500161 for (QSTile<?> tile : tiles) {
162 QSTileBaseView tileView = mQsPanel.getTileView(tile);
Jason Monk6e6af2c2016-09-20 14:34:26 -0400163 if (tileView == null) {
164 Log.e(TAG, "tileView is null " + tile.getTileSpec());
165 continue;
166 }
Jason Monk162011e2016-02-19 08:11:55 -0500167 final TextView label = ((QSTileView) tileView).getLabel();
Xiaohui Chen2f3551b2016-04-07 10:37:25 -0700168 final View tileIcon = tileView.getIcon().getIconView();
Jason Monk8fb77872016-03-03 16:39:42 -0500169 if (count < mNumQuickTiles && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500170 // Quick tiles.
171 QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
Jason Monk162011e2016-02-19 08:11:55 -0500172
Jason Monk789e9c02016-03-28 16:00:09 -0400173 lastX = loc1[0];
Jason Monk162011e2016-02-19 08:11:55 -0500174 getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
175 getRelativePosition(loc2, tileIcon, mQsContainer);
176 final int xDiff = loc2[0] - loc1[0];
177 final int yDiff = loc2[1] - loc1[1];
Jason Monk789e9c02016-03-28 16:00:09 -0400178 lastXDiff = loc1[0] - lastX;
Jason Monk162011e2016-02-19 08:11:55 -0500179 // Move the quick tile right from its location to the new one.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500180 translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
Jason Monk162011e2016-02-19 08:11:55 -0500181 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
182
183 // Counteract the parent translation on the tile. So we have a static base to
Jason Monke80654b2016-02-24 14:53:57 -0500184 // animate the label position off from.
Jason Monk162011e2016-02-19 08:11:55 -0500185 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
186
Jason Monke80654b2016-02-24 14:53:57 -0500187 // Move the real tile's label from the quick tile position to its final
Jason Monk162011e2016-02-19 08:11:55 -0500188 // location.
Jason Monkfa0f47a2016-03-08 09:17:56 -0500189 translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
Jason Monk162011e2016-02-19 08:11:55 -0500190 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
191
Jason Monke80654b2016-02-24 14:53:57 -0500192 mTopFiveQs.add(tileIcon);
193 mAllViews.add(tileIcon);
Jason Monk162011e2016-02-19 08:11:55 -0500194 mAllViews.add(quickTileView);
Jason Monk8fb77872016-03-03 16:39:42 -0500195 } else if (mFullRows && isIconInAnimatedRow(count)) {
Jason Monk789e9c02016-03-28 16:00:09 -0400196 // TODO: Refactor some of this, it shares a lot with the above block.
197 // Move the last tile position over by the last difference between quick tiles.
198 // This makes the extra icons seems as if they are coming from positions in the
199 // quick panel.
200 loc1[0] += lastXDiff;
201 getRelativePosition(loc2, tileIcon, mQsContainer);
202 final int xDiff = loc2[0] - loc1[0];
203 final int yDiff = loc2[1] - loc1[1];
204
Jason Monk8fb77872016-03-03 16:39:42 -0500205 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
Jason Monk789e9c02016-03-28 16:00:09 -0400206 translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
207 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
208 translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
209
Jason Monk8fb77872016-03-03 16:39:42 -0500210 mAllViews.add(tileIcon);
Jason Monk98196b02016-03-14 10:33:02 -0400211 } else {
Jason Monkb46059a2016-06-30 14:22:42 -0400212 firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
Jason Monk162011e2016-02-19 08:11:55 -0500213 }
214 mAllViews.add(tileView);
215 mAllViews.add(label);
Jason Monk8fb77872016-03-03 16:39:42 -0500216 count++;
Jason Monk162011e2016-02-19 08:11:55 -0500217 }
Jason Monk8fb77872016-03-03 16:39:42 -0500218 if (mAllowFancy) {
Jason Monkb46059a2016-06-30 14:22:42 -0400219 // Make brightness appear static position and alpha in through second half.
220 View brightness = mQsPanel.getBrightnessView();
221 if (brightness != null) {
222 firstPageBuilder.addFloat(brightness, "translationY", mQsPanel.getHeight(), 0);
223 mBrightnessAnimator = new TouchAnimator.Builder()
224 .addFloat(brightness, "alpha", 0, 1)
225 .setStartDelay(.5f)
226 .build();
227 mAllViews.add(brightness);
228 } else {
229 mBrightnessAnimator = null;
230 }
Jason Monk98196b02016-03-14 10:33:02 -0400231 mFirstPageAnimator = firstPageBuilder
232 .setListener(this)
233 .build();
234 // Fade in the tiles/labels as we reach the final position.
235 mFirstPageDelayedAnimator = new TouchAnimator.Builder()
236 .setStartDelay(EXPANDED_TILE_DELAY)
Jason Monkdf36aed2016-07-25 11:21:56 -0400237 .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1)
238 .addFloat(mQsPanel.getFooter().getView(), "alpha", 0, 1).build();
239 mAllViews.add(mQsPanel.getFooter().getView());
Jason Monk08a79192016-08-02 13:36:52 -0400240 float px = 0;
241 float py = 1;
242 if (tiles.size() <= 3) {
243 px = 1;
244 } else if (tiles.size() <= 6) {
245 px = .4f;
246 }
247 PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, px, py);
Jason Monka97776a2016-03-09 15:13:02 -0500248 translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
249 translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
250 mTranslationXAnimator = translationXBuilder.build();
251 mTranslationYAnimator = translationYBuilder.build();
Jason Monk8fb77872016-03-03 16:39:42 -0500252 }
Jason Monk162011e2016-02-19 08:11:55 -0500253 mNonfirstPageAnimator = new TouchAnimator.Builder()
254 .addFloat(mQuickQsPanel, "alpha", 1, 0)
Jason Monk79a9f2f2016-03-10 14:55:22 -0500255 .setListener(mNonFirstPageListener)
Jason Monk162011e2016-02-19 08:11:55 -0500256 .setEndDelay(.5f)
257 .build();
258 }
259
Jason Monk8fb77872016-03-03 16:39:42 -0500260 private boolean isIconInAnimatedRow(int count) {
261 if (mPagedLayout == null) {
262 return false;
263 }
264 final int columnCount = mPagedLayout.getColumnCount();
265 return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
266 }
267
Jason Monk162011e2016-02-19 08:11:55 -0500268 private void getRelativePosition(int[] loc1, View view, View parent) {
269 loc1[0] = 0 + view.getWidth() / 2;
270 loc1[1] = 0;
271 getRelativePositionInt(loc1, view, parent);
272 }
273
274 private void getRelativePositionInt(int[] loc1, View view, View parent) {
275 if(view == parent || view == null) return;
Jason Monk61d118d2016-05-20 14:44:16 -0400276 // Ignore tile pages as they can have some offset we don't want to take into account in
277 // RTL.
278 if (!(view instanceof PagedTileLayout.TilePage)) {
279 loc1[0] += view.getLeft();
280 loc1[1] += view.getTop();
281 }
Jason Monk162011e2016-02-19 08:11:55 -0500282 getRelativePositionInt(loc1, (View) view.getParent(), parent);
283 }
284
285 public void setPosition(float position) {
286 if (mFirstPageAnimator == null) return;
Jason Monk8fb77872016-03-03 16:39:42 -0500287 if (mOnKeyguard) {
288 return;
289 }
Jason Monkbe8612d2016-03-30 16:19:06 -0400290 mLastPosition = position;
Jason Monk8fb77872016-03-03 16:39:42 -0500291 if (mOnFirstPage && mAllowFancy) {
Jason Monk162011e2016-02-19 08:11:55 -0500292 mQuickQsPanel.setAlpha(1);
293 mFirstPageAnimator.setPosition(position);
294 mFirstPageDelayedAnimator.setPosition(position);
Jason Monka97776a2016-03-09 15:13:02 -0500295 mTranslationXAnimator.setPosition(position);
296 mTranslationYAnimator.setPosition(position);
Jason Monkb46059a2016-06-30 14:22:42 -0400297 if (mBrightnessAnimator != null) {
298 mBrightnessAnimator.setPosition(position);
299 }
Jason Monk162011e2016-02-19 08:11:55 -0500300 } else {
301 mNonfirstPageAnimator.setPosition(position);
302 }
303 }
304
305 @Override
306 public void onAnimationAtStart() {
Jason Monkdf5459d2016-02-29 10:47:35 -0500307 mQuickQsPanel.setVisibility(View.VISIBLE);
Jason Monk162011e2016-02-19 08:11:55 -0500308 }
309
310 @Override
311 public void onAnimationAtEnd() {
312 mQuickQsPanel.setVisibility(View.INVISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500313 final int N = mTopFiveQs.size();
314 for (int i = 0; i < N; i++) {
315 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
316 }
Jason Monk162011e2016-02-19 08:11:55 -0500317 }
318
319 @Override
320 public void onAnimationStarted() {
Jason Monk5737ef32016-04-12 14:35:13 -0400321 mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
Jason Monke80654b2016-02-24 14:53:57 -0500322 if (mOnFirstPage) {
323 final int N = mTopFiveQs.size();
324 for (int i = 0; i < N; i++) {
325 mTopFiveQs.get(i).setVisibility(View.INVISIBLE);
326 }
327 }
Jason Monk162011e2016-02-19 08:11:55 -0500328 }
329
330 private void clearAnimationState() {
331 final int N = mAllViews.size();
332 mQuickQsPanel.setAlpha(0);
333 for (int i = 0; i < N; i++) {
334 View v = mAllViews.get(i);
335 v.setAlpha(1);
Jason Monka2bd0462016-04-12 13:55:16 -0400336 v.setTranslationX(0);
337 v.setTranslationY(0);
Jason Monk162011e2016-02-19 08:11:55 -0500338 }
Jason Monk8fb77872016-03-03 16:39:42 -0500339 final int N2 = mTopFiveQs.size();
340 for (int i = 0; i < N2; i++) {
341 mTopFiveQs.get(i).setVisibility(View.VISIBLE);
342 }
Jason Monk162011e2016-02-19 08:11:55 -0500343 }
344
345 @Override
346 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
347 int oldTop, int oldRight, int oldBottom) {
Jason Monk51fb85a2016-03-28 14:06:04 -0400348 mQsPanel.post(mUpdateAnimators);
Jason Monk162011e2016-02-19 08:11:55 -0500349 }
350
351 @Override
352 public void onTilesChanged() {
353 // Give the QS panels a moment to generate their new tiles, then create all new animators
354 // hooked up to the new views.
355 mQsPanel.post(mUpdateAnimators);
356 }
357
Jason Monk79a9f2f2016-03-10 14:55:22 -0500358 private final TouchAnimator.Listener mNonFirstPageListener =
359 new TouchAnimator.ListenerAdapter() {
360 @Override
dongwan0605.kim03c9afa2017-01-03 15:40:07 +0900361 public void onAnimationAtEnd() {
362 mQuickQsPanel.setVisibility(View.INVISIBLE);
363 }
364
365 @Override
Jason Monk79a9f2f2016-03-10 14:55:22 -0500366 public void onAnimationStarted() {
367 mQuickQsPanel.setVisibility(View.VISIBLE);
368 }
369 };
370
Jason Monk162011e2016-02-19 08:11:55 -0500371 private Runnable mUpdateAnimators = new Runnable() {
372 @Override
373 public void run() {
374 updateAnimators();
Jason Monkbe8612d2016-03-30 16:19:06 -0400375 setPosition(mLastPosition);
Jason Monk162011e2016-02-19 08:11:55 -0500376 }
377 };
378}