Home | History | Annotate | Download | only in drawable
      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 
     47     public AnimatedRotateDrawable() {
     48         this(null, null);
     49     }
     50 
     51     private AnimatedRotateDrawable(AnimatedRotateState rotateState, Resources res) {
     52         mState = new AnimatedRotateState(rotateState, this, res);
     53         init();
     54     }
     55 
     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
     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 
     89     public void start() {
     90         if (!mRunning) {
     91             mRunning = true;
     92             nextFrame();
     93         }
     94     }
     95 
     96     public void stop() {
     97         mRunning = false;
     98         unscheduleSelf(this);
     99     }
    100 
    101     public boolean isRunning() {
    102         return mRunning;
    103     }
    104 
    105     private void nextFrame() {
    106         unscheduleSelf(this);
    107         scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
    108     }
    109 
    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
    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      */
    139     public Drawable getDrawable() {
    140         return mState.mDrawable;
    141     }
    142 
    143     @Override
    144     public int getChangingConfigurations() {
    145         return super.getChangingConfigurations()
    146                 | mState.mChangingConfigurations
    147                 | mState.mDrawable.getChangingConfigurations();
    148     }
    149 
    150     @Override
    151     public void setAlpha(int alpha) {
    152         mState.mDrawable.setAlpha(alpha);
    153     }
    154 
    155     @Override
    156     public int getAlpha() {
    157         return mState.mDrawable.getAlpha();
    158     }
    159 
    160     @Override
    161     public void setColorFilter(ColorFilter cf) {
    162         mState.mDrawable.setColorFilter(cf);
    163     }
    164 
    165     @Override
    166     public int getOpacity() {
    167         return mState.mDrawable.getOpacity();
    168     }
    169 
    170     public void invalidateDrawable(Drawable who) {
    171         final Callback callback = getCallback();
    172         if (callback != null) {
    173             callback.invalidateDrawable(this);
    174         }
    175     }
    176 
    177     public void scheduleDrawable(Drawable who, Runnable what, long when) {
    178         final Callback callback = getCallback();
    179         if (callback != null) {
    180             callback.scheduleDrawable(this, what, when);
    181         }
    182     }
    183 
    184     public void unscheduleDrawable(Drawable who, Runnable what) {
    185         final Callback callback = getCallback();
    186         if (callback != null) {
    187             callback.unscheduleDrawable(this, what);
    188         }
    189     }
    190 
    191     @Override
    192     public boolean getPadding(Rect padding) {
    193         return mState.mDrawable.getPadding(padding);
    194     }
    195 
    196     @Override
    197     public boolean isStateful() {
    198         return mState.mDrawable.isStateful();
    199     }
    200 
    201     @Override
    202     protected void onBoundsChange(Rect bounds) {
    203         mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
    204     }
    205 
    206     @Override
    207     public int getIntrinsicWidth() {
    208         return mState.mDrawable.getIntrinsicWidth();
    209     }
    210 
    211     @Override
    212     public int getIntrinsicHeight() {
    213         return mState.mDrawable.getIntrinsicHeight();
    214     }
    215 
    216     @Override
    217     public ConstantState getConstantState() {
    218         if (mState.canConstantState()) {
    219             mState.mChangingConfigurations = getChangingConfigurations();
    220             return mState;
    221         }
    222         return null;
    223     }
    224 
    225     @Override
    226     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
    227             throws XmlPullParserException, IOException {
    228 
    229         final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable);
    230 
    231         super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
    232 
    233         TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
    234         final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION;
    235         final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
    236 
    237         tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY);
    238         final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION;
    239         final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat();
    240 
    241         setFramesCount(a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12));
    242         setFramesDuration(a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150));
    243 
    244         final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
    245         Drawable drawable = null;
    246         if (res > 0) {
    247             drawable = r.getDrawable(res);
    248         }
    249 
    250         a.recycle();
    251 
    252         int outerDepth = parser.getDepth();
    253         int type;
    254         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
    255                (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    256 
    257             if (type != XmlPullParser.START_TAG) {
    258                 continue;
    259             }
    260 
    261             if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) {
    262                 Log.w("drawable", "Bad element under <animated-rotate>: "
    263                         + parser .getName());
    264             }
    265         }
    266 
    267         if (drawable == null) {
    268             Log.w("drawable", "No drawable specified for <animated-rotate>");
    269         }
    270 
    271         final AnimatedRotateState rotateState = mState;
    272         rotateState.mDrawable = drawable;
    273         rotateState.mPivotXRel = pivotXRel;
    274         rotateState.mPivotX = pivotX;
    275         rotateState.mPivotYRel = pivotYRel;
    276         rotateState.mPivotY = pivotY;
    277 
    278         init();
    279 
    280         if (drawable != null) {
    281             drawable.setCallback(this);
    282         }
    283     }
    284 
    285     public void setFramesCount(int framesCount) {
    286         mState.mFramesCount = framesCount;
    287         mIncrement = 360.0f / mState.mFramesCount;
    288     }
    289 
    290     public void setFramesDuration(int framesDuration) {
    291         mState.mFrameDuration = framesDuration;
    292     }
    293 
    294     @Override
    295     public Drawable mutate() {
    296         if (!mMutated && super.mutate() == this) {
    297             mState.mDrawable.mutate();
    298             mMutated = true;
    299         }
    300         return this;
    301     }
    302 
    303     final static class AnimatedRotateState extends Drawable.ConstantState {
    304         Drawable mDrawable;
    305 
    306         int mChangingConfigurations;
    307 
    308         boolean mPivotXRel;
    309         float mPivotX;
    310         boolean mPivotYRel;
    311         float mPivotY;
    312         int mFrameDuration;
    313         int mFramesCount;
    314 
    315         private boolean mCanConstantState;
    316         private boolean mCheckedConstantState;
    317 
    318         public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner,
    319                 Resources res) {
    320             if (source != null) {
    321                 if (res != null) {
    322                     mDrawable = source.mDrawable.getConstantState().newDrawable(res);
    323                 } else {
    324                     mDrawable = source.mDrawable.getConstantState().newDrawable();
    325                 }
    326                 mDrawable.setCallback(owner);
    327                 mDrawable.setLayoutDirection(source.mDrawable.getLayoutDirection());
    328                 mPivotXRel = source.mPivotXRel;
    329                 mPivotX = source.mPivotX;
    330                 mPivotYRel = source.mPivotYRel;
    331                 mPivotY = source.mPivotY;
    332                 mFramesCount = source.mFramesCount;
    333                 mFrameDuration = source.mFrameDuration;
    334                 mCanConstantState = mCheckedConstantState = true;
    335             }
    336         }
    337 
    338         @Override
    339         public Drawable newDrawable() {
    340             return new AnimatedRotateDrawable(this, null);
    341         }
    342 
    343         @Override
    344         public Drawable newDrawable(Resources res) {
    345             return new AnimatedRotateDrawable(this, res);
    346         }
    347 
    348         @Override
    349         public int getChangingConfigurations() {
    350             return mChangingConfigurations;
    351         }
    352 
    353         public boolean canConstantState() {
    354             if (!mCheckedConstantState) {
    355                 mCanConstantState = mDrawable.getConstantState() != null;
    356                 mCheckedConstantState = true;
    357             }
    358 
    359             return mCanConstantState;
    360         }
    361     }
    362 }
    363