/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics.drawable; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Canvas; import android.graphics.Rect; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.Resources.Theme; import android.util.AttributeSet; import android.util.TypedValue; import android.os.SystemClock; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import com.android.internal.R; /** * @hide */ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatable { private AnimatedRotateState mState; private float mCurrentDegrees; private float mIncrement; /** Whether this drawable is currently animating. */ private boolean mRunning; /** * Creates a new animated rotating drawable with no wrapped drawable. */ public AnimatedRotateDrawable() { this(new AnimatedRotateState(null), null); } @Override public void draw(Canvas canvas) { final Drawable drawable = getDrawable(); final Rect bounds = drawable.getBounds(); final int w = bounds.right - bounds.left; final int h = bounds.bottom - bounds.top; final AnimatedRotateState st = mState; final float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX; final float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY; final int saveCount = canvas.save(); canvas.rotate(mCurrentDegrees, px + bounds.left, py + bounds.top); drawable.draw(canvas); canvas.restoreToCount(saveCount); } /** * Starts the rotation animation. *
* The animation will run until {@link #stop()} is called. Calling this
* method while the animation is already running has no effect.
*
* @see #stop()
*/
@Override
public void start() {
if (!mRunning) {
mRunning = true;
nextFrame();
}
}
/**
* Stops the rotation animation.
*
* @see #start()
*/
@Override
public void stop() {
mRunning = false;
unscheduleSelf(mNextFrame);
}
@Override
public boolean isRunning() {
return mRunning;
}
private void nextFrame() {
unscheduleSelf(mNextFrame);
scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + mState.mFrameDuration);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
final boolean changed = super.setVisible(visible, restart);
if (visible) {
if (changed || restart) {
mCurrentDegrees = 0.0f;
nextFrame();
}
} else {
unscheduleSelf(mNextFrame);
}
return changed;
}
@Override
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);
updateStateFromTypedArray(a);
inflateChildDrawable(r, parser, attrs, theme);
verifyRequiredAttributes(a);
a.recycle();
updateLocalState();
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
if (getDrawable() == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ":