OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / qs / QSAnimator.java
1 /*
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
15 package com.android.systemui.qs;
16
17 import android.graphics.Path;
18 import android.util.Log;
19 import android.view.View;
20 import android.view.View.OnAttachStateChangeListener;
21 import android.view.View.OnLayoutChangeListener;
22 import android.widget.TextView;
23
24 import com.android.systemui.qs.PagedTileLayout.PageListener;
25 import com.android.systemui.qs.QSPanel.QSTileLayout;
26 import com.android.systemui.qs.QSTile.Host.Callback;
27 import com.android.systemui.qs.TouchAnimator.Builder;
28 import com.android.systemui.qs.TouchAnimator.Listener;
29 import com.android.systemui.statusbar.phone.QSTileHost;
30 import com.android.systemui.tuner.TunerService;
31 import com.android.systemui.tuner.TunerService.Tunable;
32
33 import java.util.ArrayList;
34 import java.util.Collection;
35
36 public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
37         OnAttachStateChangeListener, Tunable {
38
39     private static final String TAG = "QSAnimator";
40
41     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
44     public static final float EXPANDED_TILE_DELAY = .7f;
45     private static final float LAST_ROW_EXPANDED_DELAY = .86f;
46
47     private final ArrayList<View> mAllViews = new ArrayList<>();
48     private final ArrayList<View> mTopFiveQs = new ArrayList<>();
49     private final QuickQSPanel mQuickQsPanel;
50     private final QSPanel mQsPanel;
51     private final QSContainer mQsContainer;
52
53     private PagedTileLayout mPagedLayout;
54
55     private boolean mOnFirstPage = true;
56     private TouchAnimator mFirstPageAnimator;
57     private TouchAnimator mFirstPageDelayedAnimator;
58     private TouchAnimator mTranslationXAnimator;
59     private TouchAnimator mTranslationYAnimator;
60     private TouchAnimator mNonfirstPageAnimator;
61     private TouchAnimator mLastRowAnimator;
62
63     private boolean mOnKeyguard;
64
65     private boolean mAllowFancy;
66     private boolean mFullRows;
67     private int mNumQuickTiles;
68     private float mLastPosition;
69     private QSTileHost mHost;
70
71     public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
72         mQsContainer = container;
73         mQuickQsPanel = quickPanel;
74         mQsPanel = panel;
75         mQsPanel.addOnAttachStateChangeListener(this);
76         container.addOnLayoutChangeListener(this);
77         QSTileLayout tileLayout = mQsPanel.getTileLayout();
78         if (tileLayout instanceof PagedTileLayout) {
79             mPagedLayout = ((PagedTileLayout) tileLayout);
80             mPagedLayout.setPageListener(this);
81         } else {
82             Log.w(TAG, "QS Not using page layout");
83         }
84     }
85
86     public void onRtlChanged() {
87         updateAnimators();
88     }
89
90     public void setOnKeyguard(boolean onKeyguard) {
91         mOnKeyguard = onKeyguard;
92         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
93         if (mOnKeyguard) {
94             clearAnimationState();
95         }
96     }
97
98     public void setHost(QSTileHost qsh) {
99         mHost = qsh;
100         qsh.addCallback(this);
101         updateAnimators();
102     }
103
104     @Override
105     public void onViewAttachedToWindow(View v) {
106         TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
107                 MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
108     }
109
110     @Override
111     public void onViewDetachedFromWindow(View v) {
112         if (mHost != null) {
113             mHost.removeCallback(this);
114         }
115         TunerService.get(mQsContainer.getContext()).removeTunable(this);
116     }
117
118     @Override
119     public void onTuningChanged(String key, String newValue) {
120         if (ALLOW_FANCY_ANIMATION.equals(key)) {
121             mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
122             if (!mAllowFancy) {
123                 clearAnimationState();
124             }
125         } else if (MOVE_FULL_ROWS.equals(key)) {
126             mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
127         } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
128             mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQsContainer.getContext());
129             clearAnimationState();
130         }
131         updateAnimators();
132     }
133
134     @Override
135     public void onPageChanged(boolean isFirst) {
136         if (mOnFirstPage == isFirst) return;
137         if (!isFirst) {
138             clearAnimationState();
139         }
140         mOnFirstPage = isFirst;
141     }
142
143     private void updateAnimators() {
144         TouchAnimator.Builder firstPageBuilder = new Builder();
145         TouchAnimator.Builder translationXBuilder = new Builder();
146         TouchAnimator.Builder translationYBuilder = new Builder();
147         TouchAnimator.Builder lastRowBuilder = new Builder();
148
149         if (mQsPanel.getHost() == null) return;
150         Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
151         int count = 0;
152         int[] loc1 = new int[2];
153         int[] loc2 = new int[2];
154         int lastXDiff = 0;
155         int lastYDiff = 0;
156         int lastX = 0;
157
158         clearAnimationState();
159         mAllViews.clear();
160         mTopFiveQs.clear();
161
162         mAllViews.add((View) mQsPanel.getTileLayout());
163
164         for (QSTile<?> tile : tiles) {
165             QSTileBaseView tileView = mQsPanel.getTileView(tile);
166             final TextView label = ((QSTileView) tileView).getLabel();
167             final View tileIcon = tileView.getIcon().getIconView();
168             if (count < mNumQuickTiles && mAllowFancy) {
169                 // Quick tiles.
170                 QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
171
172                 lastX = loc1[0];
173                 getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
174                 getRelativePosition(loc2, tileIcon, mQsContainer);
175                 final int xDiff = loc2[0] - loc1[0];
176                 final int yDiff = loc2[1] - loc1[1];
177                 lastXDiff = loc1[0] - lastX;
178                 lastYDiff = yDiff;
179                 // Move the quick tile right from its location to the new one.
180                 translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
181                 translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
182
183                 // Counteract the parent translation on the tile. So we have a static base to
184                 // animate the label position off from.
185                 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
186
187                 // Move the real tile's label from the quick tile position to its final
188                 // location.
189                 translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
190                 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
191
192                 mTopFiveQs.add(tileIcon);
193                 mAllViews.add(tileIcon);
194                 mAllViews.add(quickTileView);
195             } else if (mFullRows && isIconInAnimatedRow(count)) {
196                 // 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
205                 firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
206                 translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
207                 translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
208                 translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
209
210                 mAllViews.add(tileIcon);
211             } else {
212                 lastRowBuilder.addFloat(tileView, "alpha", 0, 1);
213             }
214             mAllViews.add(tileView);
215             mAllViews.add(label);
216             count++;
217         }
218         if (mAllowFancy) {
219             mFirstPageAnimator = firstPageBuilder
220                     .setListener(this)
221                     .build();
222             // Fade in the tiles/labels as we reach the final position.
223             mFirstPageDelayedAnimator = new TouchAnimator.Builder()
224                     .setStartDelay(EXPANDED_TILE_DELAY)
225                     .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1).build();
226             mLastRowAnimator = lastRowBuilder
227                     .setStartDelay(LAST_ROW_EXPANDED_DELAY)
228                     .build();
229             Path path = new Path();
230             path.moveTo(0, 0);
231             path.cubicTo(0, 0, 0, 1, 1, 1);
232             PathInterpolatorBuilder interpolatorBuilder = new PathInterpolatorBuilder(0, 0, 0, 1);
233             translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
234             translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
235             mTranslationXAnimator = translationXBuilder.build();
236             mTranslationYAnimator = translationYBuilder.build();
237         }
238         mNonfirstPageAnimator = new TouchAnimator.Builder()
239                 .addFloat(mQuickQsPanel, "alpha", 1, 0)
240                 .setListener(mNonFirstPageListener)
241                 .setEndDelay(.5f)
242                 .build();
243     }
244
245     private boolean isIconInAnimatedRow(int count) {
246         if (mPagedLayout == null) {
247             return false;
248         }
249         final int columnCount = mPagedLayout.getColumnCount();
250         return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
251     }
252
253     private void getRelativePosition(int[] loc1, View view, View parent) {
254         loc1[0] = 0 + view.getWidth() / 2;
255         loc1[1] = 0;
256         getRelativePositionInt(loc1, view, parent);
257     }
258
259     private void getRelativePositionInt(int[] loc1, View view, View parent) {
260         if(view == parent || view == null) return;
261         // Ignore tile pages as they can have some offset we don't want to take into account in
262         // RTL.
263         if (!(view instanceof PagedTileLayout.TilePage)) {
264             loc1[0] += view.getLeft();
265             loc1[1] += view.getTop();
266         }
267         getRelativePositionInt(loc1, (View) view.getParent(), parent);
268     }
269
270     public void setPosition(float position) {
271         if (mFirstPageAnimator == null) return;
272         if (mOnKeyguard) {
273             return;
274         }
275         mLastPosition = position;
276         if (mOnFirstPage && mAllowFancy) {
277             mQuickQsPanel.setAlpha(1);
278             mFirstPageAnimator.setPosition(position);
279             mFirstPageDelayedAnimator.setPosition(position);
280             mTranslationXAnimator.setPosition(position);
281             mTranslationYAnimator.setPosition(position);
282             mLastRowAnimator.setPosition(position);
283         } else {
284             mNonfirstPageAnimator.setPosition(position);
285         }
286     }
287
288     @Override
289     public void onAnimationAtStart() {
290         mQuickQsPanel.setVisibility(View.VISIBLE);
291     }
292
293     @Override
294     public void onAnimationAtEnd() {
295         mQuickQsPanel.setVisibility(View.INVISIBLE);
296         final int N = mTopFiveQs.size();
297         for (int i = 0; i < N; i++) {
298             mTopFiveQs.get(i).setVisibility(View.VISIBLE);
299         }
300     }
301
302     @Override
303     public void onAnimationStarted() {
304         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
305         if (mOnFirstPage) {
306             final int N = mTopFiveQs.size();
307             for (int i = 0; i < N; i++) {
308                 mTopFiveQs.get(i).setVisibility(View.INVISIBLE);
309             }
310         }
311     }
312
313     private void clearAnimationState() {
314         final int N = mAllViews.size();
315         mQuickQsPanel.setAlpha(0);
316         for (int i = 0; i < N; i++) {
317             View v = mAllViews.get(i);
318             v.setAlpha(1);
319             v.setTranslationX(0);
320             v.setTranslationY(0);
321         }
322         final int N2 = mTopFiveQs.size();
323         for (int i = 0; i < N2; i++) {
324             mTopFiveQs.get(i).setVisibility(View.VISIBLE);
325         }
326     }
327
328     @Override
329     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
330             int oldTop, int oldRight, int oldBottom) {
331         mQsPanel.post(mUpdateAnimators);
332     }
333
334     @Override
335     public void onTilesChanged() {
336         // Give the QS panels a moment to generate their new tiles, then create all new animators
337         // hooked up to the new views.
338         mQsPanel.post(mUpdateAnimators);
339     }
340
341     private final TouchAnimator.Listener mNonFirstPageListener =
342             new TouchAnimator.ListenerAdapter() {
343                 @Override
344                 public void onAnimationStarted() {
345                     mQuickQsPanel.setVisibility(View.VISIBLE);
346                 }
347             };
348
349     private Runnable mUpdateAnimators = new Runnable() {
350         @Override
351         public void run() {
352             updateAnimators();
353             setPosition(mLastPosition);
354         }
355     };
356 }