• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.systemui.qs;
2 
3 import android.content.Context;
4 import android.graphics.drawable.AnimatedVectorDrawable;
5 import android.util.AttributeSet;
6 import android.util.Log;
7 import android.view.View;
8 import android.view.ViewGroup;
9 import android.widget.ImageView;
10 import com.android.systemui.R;
11 
12 import java.util.ArrayList;
13 
14 public class PageIndicator extends ViewGroup {
15 
16     private static final String TAG = "PageIndicator";
17     private static final boolean DEBUG = false;
18 
19     private static final long ANIMATION_DURATION = 250;
20 
21     // The size of a single dot in relation to the whole animation.
22     private static final float SINGLE_SCALE = .4f;
23 
24     private static final float MINOR_ALPHA = .3f;
25 
26     private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
27 
28     private final int mPageIndicatorWidth;
29     private final int mPageIndicatorHeight;
30     private final int mPageDotWidth;
31 
32     private int mPosition = -1;
33     private boolean mAnimating;
34 
PageIndicator(Context context, AttributeSet attrs)35     public PageIndicator(Context context, AttributeSet attrs) {
36         super(context, attrs);
37         mPageIndicatorWidth =
38                 (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
39         mPageIndicatorHeight =
40                 (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
41         mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
42     }
43 
setNumPages(int numPages)44     public void setNumPages(int numPages) {
45         setVisibility(numPages > 1 ? View.VISIBLE : View.INVISIBLE);
46         if (mAnimating) {
47             Log.w(TAG, "setNumPages during animation");
48         }
49         while (numPages < getChildCount()) {
50             removeViewAt(getChildCount() - 1);
51         }
52         while (numPages > getChildCount()) {
53             ImageView v = new ImageView(mContext);
54             v.setImageResource(R.drawable.minor_a_b);
55             addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
56         }
57         // Refresh state.
58         setIndex(mPosition >> 1);
59     }
60 
setLocation(float location)61     public void setLocation(float location) {
62         int index = (int) location;
63         setContentDescription(getContext().getString(R.string.accessibility_quick_settings_page,
64                 (index + 1), getChildCount()));
65         int position = index << 1 | ((location != index) ? 1 : 0);
66         if (DEBUG) Log.d(TAG, "setLocation " + location + " " + index + " " + position);
67 
68         int lastPosition = mPosition;
69         if (mQueuedPositions.size() != 0) {
70             lastPosition = mQueuedPositions.get(mQueuedPositions.size() - 1);
71         }
72         if (position == lastPosition) return;
73         if (mAnimating) {
74             if (DEBUG) Log.d(TAG, "Queueing transition to " + Integer.toHexString(position));
75             mQueuedPositions.add(position);
76             return;
77         }
78 
79         setPosition(position);
80     }
81 
setPosition(int position)82     private void setPosition(int position) {
83         if (isVisibleToUser() && Math.abs(mPosition - position) == 1) {
84             animate(mPosition, position);
85         } else {
86             if (DEBUG) Log.d(TAG, "Skipping animation " + isVisibleToUser() + " " + mPosition
87                     + " " + position);
88             setIndex(position >> 1);
89         }
90         mPosition = position;
91     }
92 
setIndex(int index)93     private void setIndex(int index) {
94         final int N = getChildCount();
95         for (int i = 0; i < N; i++) {
96             ImageView v = (ImageView) getChildAt(i);
97             // Clear out any animation positioning.
98             v.setTranslationX(0);
99             v.setImageResource(R.drawable.major_a_b);
100             v.setAlpha(getAlpha(i == index));
101         }
102     }
103 
animate(int from, int to)104     private void animate(int from, int to) {
105         if (DEBUG) Log.d(TAG, "Animating from " + Integer.toHexString(from) + " to "
106                 + Integer.toHexString(to));
107         int fromIndex = from >> 1;
108         int toIndex = to >> 1;
109 
110         // Set the position of everything, then we will manually control the two views involved
111         // in the animation.
112         setIndex(fromIndex);
113 
114         boolean fromTransition = (from & 1) != 0;
115         boolean isAState = fromTransition ? from > to : from < to;
116         int firstIndex = Math.min(fromIndex, toIndex);
117         int secondIndex = Math.max(fromIndex, toIndex);
118         if (secondIndex == firstIndex) {
119             secondIndex++;
120         }
121         ImageView first = (ImageView) getChildAt(firstIndex);
122         ImageView second = (ImageView) getChildAt(secondIndex);
123         if (first == null || second == null) {
124             // may happen during reInflation or other weird cases
125             return;
126         }
127         // Lay the two views on top of each other.
128         second.setTranslationX(first.getX() - second.getX());
129 
130         playAnimation(first, getTransition(fromTransition, isAState, false));
131         first.setAlpha(getAlpha(false));
132 
133         playAnimation(second, getTransition(fromTransition, isAState, true));
134         second.setAlpha(getAlpha(true));
135 
136         mAnimating = true;
137     }
138 
139     private float getAlpha(boolean isMajor) {
140         return isMajor ? 1 : MINOR_ALPHA;
141     }
142 
143     private void playAnimation(ImageView imageView, int res) {
144         final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext().getDrawable(res);
145         imageView.setImageDrawable(avd);
146         avd.forceAnimationOnUI();
147         avd.start();
148         // TODO: Figure out how to user an AVD animation callback instead, which doesn't
149         // seem to be working right now...
150         postDelayed(mAnimationDone, ANIMATION_DURATION);
151     }
152 
153     private int getTransition(boolean fromB, boolean isMajorAState, boolean isMajor) {
154         if (isMajor) {
155             if (fromB) {
156                 if (isMajorAState) {
157                     return R.drawable.major_b_a_animation;
158                 } else {
159                     return R.drawable.major_b_c_animation;
160                 }
161             } else {
162                 if (isMajorAState) {
163                     return R.drawable.major_a_b_animation;
164                 } else {
165                     return R.drawable.major_c_b_animation;
166                 }
167             }
168         } else {
169             if (fromB) {
170                 if (isMajorAState) {
171                     return R.drawable.minor_b_c_animation;
172                 } else {
173                     return R.drawable.minor_b_a_animation;
174                 }
175             } else {
176                 if (isMajorAState) {
177                     return R.drawable.minor_c_b_animation;
178                 } else {
179                     return R.drawable.minor_a_b_animation;
180                 }
181             }
182         }
183     }
184 
185     @Override
186     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
187         final int N = getChildCount();
188         if (N == 0) {
189             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
190             return;
191         }
192         final int widthChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorWidth,
193                 MeasureSpec.EXACTLY);
194         final int heightChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorHeight,
195                 MeasureSpec.EXACTLY);
196         for (int i = 0; i < N; i++) {
197             getChildAt(i).measure(widthChildSpec, heightChildSpec);
198         }
199         int width = (mPageIndicatorWidth - mPageDotWidth) * N + mPageDotWidth;
200         setMeasuredDimension(width, mPageIndicatorHeight);
201     }
202 
203     @Override
204     protected void onLayout(boolean changed, int l, int t, int r, int b) {
205         final int N = getChildCount();
206         if (N == 0) {
207             return;
208         }
209         for (int i = 0; i < N; i++) {
210             int left = (mPageIndicatorWidth - mPageDotWidth) * i;
211             getChildAt(i).layout(left, 0, mPageIndicatorWidth + left, mPageIndicatorHeight);
212         }
213     }
214 
215     private final Runnable mAnimationDone = new Runnable() {
216         @Override
217         public void run() {
218             if (DEBUG) Log.d(TAG, "onAnimationEnd - queued: " + mQueuedPositions.size());
219             mAnimating = false;
220             if (mQueuedPositions.size() != 0) {
221                 setPosition(mQueuedPositions.remove(0));
222             }
223         }
224     };
225 }
226