• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics.drawable;
18 
19 import android.graphics.Canvas;
20 import android.graphics.Rect;
21 import android.graphics.ColorFilter;
22 import android.content.res.Resources;
23 import android.content.res.TypedArray;
24 import android.util.AttributeSet;
25 import android.util.TypedValue;
26 import android.util.Log;
27 import android.os.SystemClock;
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 
31 import java.io.IOException;
32 
33 import com.android.internal.R;
34 
35 /**
36  * @hide
37  */
38 public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable,
39         Animatable {
40 
41     private AnimatedRotateState mState;
42     private boolean mMutated;
43     private float mCurrentDegrees;
44     private float mIncrement;
45     private boolean mRunning;
46 
AnimatedRotateDrawable()47     public AnimatedRotateDrawable() {
48         this(null, null);
49     }
50 
AnimatedRotateDrawable(AnimatedRotateState rotateState, Resources res)51     private AnimatedRotateDrawable(AnimatedRotateState rotateState, Resources res) {
52         mState = new AnimatedRotateState(rotateState, this, res);
53         init();
54     }
55 
init()56     private void init() {
57         final AnimatedRotateState state = mState;
58         mIncrement = 360.0f / state.mFramesCount;
59         final Drawable drawable = state.mDrawable;
60         if (drawable != null) {
61             drawable.setFilterBitmap(true);
62             if (drawable instanceof BitmapDrawable) {
63                 ((BitmapDrawable) drawable).setAntiAlias(true);
64             }
65         }
66     }
67 
68     @Override
draw(Canvas canvas)69     public void draw(Canvas canvas) {
70         int saveCount = canvas.save();
71 
72         final AnimatedRotateState st = mState;
73         final Drawable drawable = st.mDrawable;
74         final Rect bounds = drawable.getBounds();
75 
76         int w = bounds.right - bounds.left;
77         int h = bounds.bottom - bounds.top;
78 
79         float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX;
80         float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY;
81 
82         canvas.rotate(mCurrentDegrees, px + bounds.left, py + bounds.top);
83 
84         drawable.draw(canvas);
85 
86         canvas.restoreToCount(saveCount);
87     }
88 
start()89     public void start() {
90         if (!mRunning) {
91             mRunning = true;
92             nextFrame();
93         }
94     }
95 
stop()96     public void stop() {
97         mRunning = false;
98         unscheduleSelf(this);
99     }
100 
isRunning()101     public boolean isRunning() {
102         return mRunning;
103     }
104 
nextFrame()105     private void nextFrame() {
106         unscheduleSelf(this);
107         scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
108     }
109 
run()110     public void run() {
111         // TODO: This should be computed in draw(Canvas), based on the amount
112         // of time since the last frame drawn
113         mCurrentDegrees += mIncrement;
114         if (mCurrentDegrees > (360.0f - mIncrement)) {
115             mCurrentDegrees = 0.0f;
116         }
117         invalidateSelf();
118         nextFrame();
119     }
120 
121     @Override
setVisible(boolean visible, boolean restart)122     public boolean setVisible(boolean visible, boolean restart) {
123         mState.mDrawable.setVisible(visible, restart);
124         boolean changed = super.setVisible(visible, restart);
125         if (visible) {
126             if (changed || restart) {
127                 mCurrentDegrees = 0.0f;
128                 nextFrame();
129             }
130         } else {
131             unscheduleSelf(this);
132         }
133         return changed;
134     }
135 
136     /**
137      * Returns the drawable rotated by this RotateDrawable.
138      */
getDrawable()139     public Drawable getDrawable() {
140         return mState.mDrawable;
141     }
142 
143     @Override
getChangingConfigurations()144     public int getChangingConfigurations() {
145         return super.getChangingConfigurations()
146                 | mState.mChangingConfigurations
147                 | mState.mDrawable.getChangingConfigurations();
148     }
149 
150     @Override
setAlpha(int alpha)151     public void setAlpha(int alpha) {
152         mState.mDrawable.setAlpha(alpha);
153     }
154 
155     @Override
setColorFilter(ColorFilter cf)156     public void setColorFilter(ColorFilter cf) {
157         mState.mDrawable.setColorFilter(cf);
158     }
159 
160     @Override
getOpacity()161     public int getOpacity() {
162         return mState.mDrawable.getOpacity();
163     }
164 
invalidateDrawable(Drawable who)165     public void invalidateDrawable(Drawable who) {
166         final Callback callback = getCallback();
167         if (callback != null) {
168             callback.invalidateDrawable(this);
169         }
170     }
171 
scheduleDrawable(Drawable who, Runnable what, long when)172     public void scheduleDrawable(Drawable who, Runnable what, long when) {
173         final Callback callback = getCallback();
174         if (callback != null) {
175             callback.scheduleDrawable(this, what, when);
176         }
177     }
178 
unscheduleDrawable(Drawable who, Runnable what)179     public void unscheduleDrawable(Drawable who, Runnable what) {
180         final Callback callback = getCallback();
181         if (callback != null) {
182             callback.unscheduleDrawable(this, what);
183         }
184     }
185 
186     @Override
getPadding(Rect padding)187     public boolean getPadding(Rect padding) {
188         return mState.mDrawable.getPadding(padding);
189     }
190 
191     @Override
isStateful()192     public boolean isStateful() {
193         return mState.mDrawable.isStateful();
194     }
195 
196     @Override
onBoundsChange(Rect bounds)197     protected void onBoundsChange(Rect bounds) {
198         mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
199     }
200 
201     @Override
getIntrinsicWidth()202     public int getIntrinsicWidth() {
203         return mState.mDrawable.getIntrinsicWidth();
204     }
205 
206     @Override
getIntrinsicHeight()207     public int getIntrinsicHeight() {
208         return mState.mDrawable.getIntrinsicHeight();
209     }
210 
211     @Override
getConstantState()212     public ConstantState getConstantState() {
213         if (mState.canConstantState()) {
214             mState.mChangingConfigurations = getChangingConfigurations();
215             return mState;
216         }
217         return null;
218     }
219 
220     @Override
inflate(Resources r, XmlPullParser parser, AttributeSet attrs)221     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
222             throws XmlPullParserException, IOException {
223 
224         final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
225 
226         super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
227 
228         TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
229         final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
230         final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
231 
232         tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
233         final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
234         final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
235 
236         setFramesCount(a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12));
237         setFramesDuration(a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150));
238 
239         final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
240         Drawable drawable = null;
241         if (res > 0) {
242             drawable = r.getDrawable(res);
243         }
244 
245         a.recycle();
246 
247         int outerDepth = parser.getDepth();
248         int type;
249         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
250                (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
251 
252             if (type != XmlPullParser.START_TAG) {
253                 continue;
254             }
255 
256             if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
257                 Log.w("drawable", "Bad element under <animated-rotate>: "
258                         + parser .getName());
259             }
260         }
261 
262         if (drawable == null) {
263             Log.w("drawable", "No drawable specified for <animated-rotate>");
264         }
265 
266         final AnimatedRotateState rotateState = mState;
267         rotateState.mDrawable = drawable;
268         rotateState.mPivotXRel = pivotXRel;
269         rotateState.mPivotX = pivotX;
270         rotateState.mPivotYRel = pivotYRel;
271         rotateState.mPivotY = pivotY;
272 
273         init();
274 
275         if (drawable != null) {
276             drawable.setCallback(this);
277         }
278     }
279 
setFramesCount(int framesCount)280     public void setFramesCount(int framesCount) {
281         mState.mFramesCount = framesCount;
282         mIncrement = 360.0f / mState.mFramesCount;
283     }
284 
setFramesDuration(int framesDuration)285     public void setFramesDuration(int framesDuration) {
286         mState.mFrameDuration = framesDuration;
287     }
288 
289     @Override
mutate()290     public Drawable mutate() {
291         if (!mMutated && super.mutate() == this) {
292             mState.mDrawable.mutate();
293             mMutated = true;
294         }
295         return this;
296     }
297 
298     final static class AnimatedRotateState extends Drawable.ConstantState {
299         Drawable mDrawable;
300 
301         int mChangingConfigurations;
302 
303         boolean mPivotXRel;
304         float mPivotX;
305         boolean mPivotYRel;
306         float mPivotY;
307         int mFrameDuration;
308         int mFramesCount;
309 
310         private boolean mCanConstantState;
311         private boolean mCheckedConstantState;
312 
AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner, Resources res)313         public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner,
314                 Resources res) {
315             if (source != null) {
316                 if (res != null) {
317                     mDrawable = source.mDrawable.getConstantState().newDrawable(res);
318                 } else {
319                     mDrawable = source.mDrawable.getConstantState().newDrawable();
320                 }
321                 mDrawable.setCallback(owner);
322                 mPivotXRel = source.mPivotXRel;
323                 mPivotX = source.mPivotX;
324                 mPivotYRel = source.mPivotYRel;
325                 mPivotY = source.mPivotY;
326                 mFramesCount = source.mFramesCount;
327                 mFrameDuration = source.mFrameDuration;
328                 mCanConstantState = mCheckedConstantState = true;
329             }
330         }
331 
332         @Override
newDrawable()333         public Drawable newDrawable() {
334             return new AnimatedRotateDrawable(this, null);
335         }
336 
337         @Override
newDrawable(Resources res)338         public Drawable newDrawable(Resources res) {
339             return new AnimatedRotateDrawable(this, res);
340         }
341 
342         @Override
getChangingConfigurations()343         public int getChangingConfigurations() {
344             return mChangingConfigurations;
345         }
346 
canConstantState()347         public boolean canConstantState() {
348             if (!mCheckedConstantState) {
349                 mCanConstantState = mDrawable.getConstantState() != null;
350                 mCheckedConstantState = true;
351             }
352 
353             return mCanConstantState;
354         }
355     }
356 }
357