• 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 / (float) 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 
draw(Canvas canvas)68     public void draw(Canvas canvas) {
69         int saveCount = canvas.save();
70 
71         final AnimatedRotateState st = mState;
72         final Drawable drawable = st.mDrawable;
73         final Rect bounds = drawable.getBounds();
74 
75         int w = bounds.right - bounds.left;
76         int h = bounds.bottom - bounds.top;
77 
78         float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX;
79         float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY;
80 
81         canvas.rotate(mCurrentDegrees, px, py);
82 
83         drawable.draw(canvas);
84 
85         canvas.restoreToCount(saveCount);
86     }
87 
start()88     public void start() {
89         if (!mRunning) {
90             mRunning = true;
91             nextFrame();
92         }
93     }
94 
stop()95     public void stop() {
96         mRunning = false;
97         unscheduleSelf(this);
98     }
99 
isRunning()100     public boolean isRunning() {
101         return mRunning;
102     }
103 
nextFrame()104     private void nextFrame() {
105         unscheduleSelf(this);
106         scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
107     }
108 
run()109     public void run() {
110         // TODO: This should be computed in draw(Canvas), based on the amount
111         // of time since the last frame drawn
112         mCurrentDegrees += mIncrement;
113         if (mCurrentDegrees > (360.0f - mIncrement)) {
114             mCurrentDegrees = 0.0f;
115         }
116         invalidateSelf();
117         nextFrame();
118     }
119 
120     @Override
setVisible(boolean visible, boolean restart)121     public boolean setVisible(boolean visible, boolean restart) {
122         mState.mDrawable.setVisible(visible, restart);
123         boolean changed = super.setVisible(visible, restart);
124         if (visible) {
125             if (changed || restart) {
126                 mCurrentDegrees = 0.0f;
127                 nextFrame();
128             }
129         } else {
130             unscheduleSelf(this);
131         }
132         return changed;
133     }
134 
135     /**
136      * Returns the drawable rotated by this RotateDrawable.
137      */
getDrawable()138     public Drawable getDrawable() {
139         return mState.mDrawable;
140     }
141 
142     @Override
getChangingConfigurations()143     public int getChangingConfigurations() {
144         return super.getChangingConfigurations()
145                 | mState.mChangingConfigurations
146                 | mState.mDrawable.getChangingConfigurations();
147     }
148 
setAlpha(int alpha)149     public void setAlpha(int alpha) {
150         mState.mDrawable.setAlpha(alpha);
151     }
152 
setColorFilter(ColorFilter cf)153     public void setColorFilter(ColorFilter cf) {
154         mState.mDrawable.setColorFilter(cf);
155     }
156 
getOpacity()157     public int getOpacity() {
158         return mState.mDrawable.getOpacity();
159     }
160 
invalidateDrawable(Drawable who)161     public void invalidateDrawable(Drawable who) {
162         if (mCallback != null) {
163             mCallback.invalidateDrawable(this);
164         }
165     }
166 
scheduleDrawable(Drawable who, Runnable what, long when)167     public void scheduleDrawable(Drawable who, Runnable what, long when) {
168         if (mCallback != null) {
169             mCallback.scheduleDrawable(this, what, when);
170         }
171     }
172 
unscheduleDrawable(Drawable who, Runnable what)173     public void unscheduleDrawable(Drawable who, Runnable what) {
174         if (mCallback != null) {
175             mCallback.unscheduleDrawable(this, what);
176         }
177     }
178 
179     @Override
getPadding(Rect padding)180     public boolean getPadding(Rect padding) {
181         return mState.mDrawable.getPadding(padding);
182     }
183 
184     @Override
isStateful()185     public boolean isStateful() {
186         return mState.mDrawable.isStateful();
187     }
188 
189     @Override
onBoundsChange(Rect bounds)190     protected void onBoundsChange(Rect bounds) {
191         mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
192     }
193 
194     @Override
getIntrinsicWidth()195     public int getIntrinsicWidth() {
196         return mState.mDrawable.getIntrinsicWidth();
197     }
198 
199     @Override
getIntrinsicHeight()200     public int getIntrinsicHeight() {
201         return mState.mDrawable.getIntrinsicHeight();
202     }
203 
204     @Override
getConstantState()205     public ConstantState getConstantState() {
206         if (mState.canConstantState()) {
207             mState.mChangingConfigurations = super.getChangingConfigurations();
208             return mState;
209         }
210         return null;
211     }
212 
213     @Override
inflate(Resources r, XmlPullParser parser, AttributeSet attrs)214     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
215             throws XmlPullParserException, IOException {
216 
217         final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
218 
219         super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
220 
221         TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
222         final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
223         final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
224 
225         tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
226         final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
227         final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
228 
229         final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12);
230         final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150);
231 
232         final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
233         Drawable drawable = null;
234         if (res > 0) {
235             drawable = r.getDrawable(res);
236         }
237 
238         a.recycle();
239 
240         int outerDepth = parser.getDepth();
241         int type;
242         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
243                (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
244 
245             if (type != XmlPullParser.START_TAG) {
246                 continue;
247             }
248 
249             if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
250                 Log.w("drawable", "Bad element under <animated-rotate>: "
251                         + parser .getName());
252             }
253         }
254 
255         if (drawable == null) {
256             Log.w("drawable", "No drawable specified for <animated-rotate>");
257         }
258 
259         final AnimatedRotateState rotateState = mState;
260         rotateState.mDrawable = drawable;
261         rotateState.mPivotXRel = pivotXRel;
262         rotateState.mPivotX = pivotX;
263         rotateState.mPivotYRel = pivotYRel;
264         rotateState.mPivotY = pivotY;
265         rotateState.mFramesCount = framesCount;
266         rotateState.mFrameDuration = frameDuration;
267 
268         init();
269 
270         if (drawable != null) {
271             drawable.setCallback(this);
272         }
273     }
274 
275     @Override
mutate()276     public Drawable mutate() {
277         if (!mMutated && super.mutate() == this) {
278             mState.mDrawable.mutate();
279             mMutated = true;
280         }
281         return this;
282     }
283 
284     final static class AnimatedRotateState extends Drawable.ConstantState {
285         Drawable mDrawable;
286 
287         int mChangingConfigurations;
288 
289         boolean mPivotXRel;
290         float mPivotX;
291         boolean mPivotYRel;
292         float mPivotY;
293         int mFrameDuration;
294         int mFramesCount;
295 
296         private boolean mCanConstantState;
297         private boolean mCheckedConstantState;
298 
AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner, Resources res)299         public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner,
300                 Resources res) {
301             if (source != null) {
302                 if (res != null) {
303                     mDrawable = source.mDrawable.getConstantState().newDrawable(res);
304                 } else {
305                     mDrawable = source.mDrawable.getConstantState().newDrawable();
306                 }
307                 mDrawable.setCallback(owner);
308                 mPivotXRel = source.mPivotXRel;
309                 mPivotX = source.mPivotX;
310                 mPivotYRel = source.mPivotYRel;
311                 mPivotY = source.mPivotY;
312                 mFramesCount = source.mFramesCount;
313                 mFrameDuration = source.mFrameDuration;
314                 mCanConstantState = mCheckedConstantState = true;
315             }
316         }
317 
318         @Override
newDrawable()319         public Drawable newDrawable() {
320             return new AnimatedRotateDrawable(this, null);
321         }
322 
323         @Override
newDrawable(Resources res)324         public Drawable newDrawable(Resources res) {
325             return new AnimatedRotateDrawable(this, res);
326         }
327 
328         @Override
getChangingConfigurations()329         public int getChangingConfigurations() {
330             return mChangingConfigurations;
331         }
332 
canConstantState()333         public boolean canConstantState() {
334             if (!mCheckedConstantState) {
335                 mCanConstantState = mDrawable.getConstantState() != null;
336                 mCheckedConstantState = true;
337             }
338 
339             return mCanConstantState;
340         }
341     }
342 }
343