diff --git a/src/api-impl/android/graphics/drawable/AnimationDrawable.java b/src/api-impl/android/graphics/drawable/AnimationDrawable.java
index daf18c3a..59e6e409 100644
--- a/src/api-impl/android/graphics/drawable/AnimationDrawable.java
+++ b/src/api-impl/android/graphics/drawable/AnimationDrawable.java
@@ -1,21 +1,362 @@
+/*
+ * Copyright (C) 2006 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;
-public class AnimationDrawable extends Drawable {
- private int num_frames = 0;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import java.io.IOException;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
- public int getNumberOfFrames() {
- return num_frames;
+/**
+ *
+ * An object used to create frame-by-frame animations, defined by a series of Drawable objects,
+ * which can be used as a View object's background.
+ *
+ * The simplest way to create a frame-by-frame animation is to define the animation in an XML
+ * file, placed in the res/drawable/ folder, and set it as the background to a View object. Then, call
+ * {@link #start()} to run the animation.
+ *
+ * An AnimationDrawable defined in XML consists of a single <animation-list> element,
+ * and a series of nested <item> tags. Each item defines a frame of the animation.
+ * See the example below.
+ *
+ * spin_animation.xml file in res/drawable/ folder:
+ * <!-- Animation frames are wheel0.png -- wheel5.png files inside the
+ * res/drawable/ folder -->
+ * <animation-list android:id="@+id/selected" android:oneshot="false">
+ * <item android:drawable="@drawable/wheel0" android:duration="50" />
+ * <item android:drawable="@drawable/wheel1" android:duration="50" />
+ * <item android:drawable="@drawable/wheel2" android:duration="50" />
+ * <item android:drawable="@drawable/wheel3" android:duration="50" />
+ * <item android:drawable="@drawable/wheel4" android:duration="50" />
+ * <item android:drawable="@drawable/wheel5" android:duration="50" />
+ * </animation-list>
+ *
+ * Here is the code to load and play this animation.
+ *
+ * // Load the ImageView that will host the animation and
+ * // set its background to our AnimationDrawable XML resource.
+ * ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
+ * img.setBackgroundResource(R.drawable.spin_animation);
+ *
+ * // Get the background, which has been compiled to an AnimationDrawable object.
+ * AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();
+ *
+ * // Start the animation (looped playback by default).
+ * frameAnimation.start();
+ *
+ *
+ *
+ *
Developer Guides
+ *
For more information about animating with {@code AnimationDrawable}, read the
+ * Drawable Animation
+ * developer guide.
+ *
+ *
+ * @attr ref android.R.styleable#AnimationDrawable_visible
+ * @attr ref android.R.styleable#AnimationDrawable_variablePadding
+ * @attr ref android.R.styleable#AnimationDrawable_oneshot
+ * @attr ref android.R.styleable#AnimationDrawableItem_duration
+ * @attr ref android.R.styleable#AnimationDrawableItem_drawable
+ */
+public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
+ private final AnimationState mAnimationState;
+ private int mCurFrame = -1;
+ private boolean mMutated;
+
+ public AnimationDrawable() {
+ this(null, null);
}
- public void addFrame(Drawable drawable, int duration) {
- num_frames++;
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (visible) {
+ if (changed || restart) {
+ setFrame(0, true, true);
+ }
+ } else {
+ unscheduleSelf(this);
+ }
+ return changed;
}
+
+ /**
+ * Starts the animation, looping if necessary. This method has no effect
+ * if the animation is running. Do not call this in the {@link android.app.Activity#onCreate}
+ * method of your activity, because the {@link android.graphics.drawable.AnimationDrawable} is
+ * not yet fully attached to the window. If you want to play
+ * the animation immediately, without requiring interaction, then you might want to call it
+ * from the {@link android.app.Activity#onWindowFocusChanged} method in your activity,
+ * which will get called when Android brings your window into focus.
+ *
+ * @see #isRunning()
+ * @see #stop()
+ */
public void start() {
- for(int i = 0; i < num_frames; i++) {
+ if (!isRunning()) {
run();
}
}
- public void stop() {}
- public void run() {}
+ /**
+ * Stops the animation. This method has no effect if the animation is
+ * not running.
+ *
+ * @see #isRunning()
+ * @see #start()
+ */
+ public void stop() {
+ if (isRunning()) {
+ unscheduleSelf(this);
+ }
+ }
+
+ /**
+ * Indicates whether the animation is currently running or not.
+ *
+ * @return true if the animation is running, false otherwise
+ */
+ public boolean isRunning() {
+ return mCurFrame > -1;
+ }
+
+ /**
+ * This method exists for implementation purpose only and should not be
+ * called directly. Invoke {@link #start()} instead.
+ *
+ * @see #start()
+ */
+ public void run() {
+ nextFrame(false);
+ }
+
+// @Override
+ public void unscheduleSelf(Runnable what) {
+ mCurFrame = -1;
+ super.unscheduleSelf(what);
+ }
+
+ /**
+ * @return The number of frames in the animation
+ */
+ public int getNumberOfFrames() {
+ return mAnimationState.getChildCount();
+ }
+
+ /**
+ * @return The Drawable at the specified frame index
+ */
+ public Drawable getFrame(int index) {
+ return mAnimationState.getChild(index);
+ }
+
+ /**
+ * @return The duration in milliseconds of the frame at the
+ * specified index
+ */
+ public int getDuration(int i) {
+ return mAnimationState.mDurations[i];
+ }
+
+ /**
+ * @return True of the animation will play once, false otherwise
+ */
+ public boolean isOneShot() {
+ return mAnimationState.mOneShot;
+ }
+
+ /**
+ * Sets whether the animation should play once or repeat.
+ *
+ * @param oneShot Pass true if the animation should only play once
+ */
+ public void setOneShot(boolean oneShot) {
+ mAnimationState.mOneShot = oneShot;
+ }
+
+ /**
+ * Add a frame to the animation
+ *
+ * @param frame The frame to add
+ * @param duration How long in milliseconds the frame should appear
+ */
+ public void addFrame(Drawable frame, int duration) {
+ mAnimationState.addFrame(frame, duration);
+ if (mCurFrame < 0) {
+ setFrame(0, true, false);
+ }
+ }
+
+ private void nextFrame(boolean unschedule) {
+ int next = mCurFrame + 1;
+ final int N = mAnimationState.getChildCount();
+ if (next >= N) {
+ next = 0;
+ }
+ setFrame(next, unschedule, !mAnimationState.mOneShot || next < (N - 1));
+ }
+
+ private void setFrame(int frame, boolean unschedule, boolean animate) {
+ if (frame >= mAnimationState.getChildCount()) {
+ return;
+ }
+ mCurFrame = frame;
+ selectDrawable(frame);
+ if (unschedule) {
+ unscheduleSelf(this);
+ }
+ if (animate) {
+ // Unscheduling may have clobbered this value; restore it to record that we're animating
+ mCurFrame = frame;
+ scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
+ }
+ }
+
+// @Override
+/* public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
+
+ TypedArray a = r.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AnimationDrawable);
+
+ super.inflateWithAttributes(r, parser, a,
+ com.android.internal.R.styleable.AnimationDrawable_visible);
+
+ mAnimationState.setVariablePadding(a.getBoolean(
+ com.android.internal.R.styleable.AnimationDrawable_variablePadding, false));
+
+ mAnimationState.mOneShot = a.getBoolean(
+ com.android.internal.R.styleable.AnimationDrawable_oneshot, false);
+
+ a.recycle();
+
+ int type;
+
+ final int innerDepth = parser.getDepth() + 1;
+ int depth;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT &&
+ ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ a = r.obtainAttributes(attrs, com.android.internal.R.styleable.AnimationDrawableItem);
+ int duration = a.getInt(
+ com.android.internal.R.styleable.AnimationDrawableItem_duration, -1);
+ if (duration < 0) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription() + ": - tag requires a 'duration' attribute");
+ }
+ int drawableRes = a.getResourceId(
+ com.android.internal.R.styleable.AnimationDrawableItem_drawable, 0);
+
+ a.recycle();
+
+ Drawable dr;
+ if (drawableRes != 0) {
+ dr = r.getDrawable(drawableRes);
+ } else {
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ // Empty
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(parser.getPositionDescription() +
+ ":
- tag requires a 'drawable' attribute or child tag"
+ +
+ " defining a drawable");
+ }
+ dr = Drawable.createFromXmlInner(r, parser, attrs);
+ }
+
+ mAnimationState.addFrame(dr, duration);
+ if (dr != null) {
+ dr.setCallback(this);
+ }
+ }
+
+ setFrame(0, true, false);
+ }
+*/
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mAnimationState.mDurations = mAnimationState.mDurations.clone();
+ mMutated = true;
+ }
+ return this;
+ }
+
+ private final static class AnimationState extends DrawableContainerState {
+ private int[] mDurations;
+ private boolean mOneShot;
+
+ AnimationState(AnimationState orig, AnimationDrawable owner,
+ Resources res) {
+ super(orig, owner, res);
+
+ if (orig != null) {
+ mDurations = orig.mDurations;
+ mOneShot = orig.mOneShot;
+ } else {
+ mDurations = new int[getCapacity()];
+ mOneShot = true;
+ }
+ }
+
+// @Override
+ public Drawable newDrawable() {
+ return new AnimationDrawable(this, null);
+ }
+
+// @Override
+ public Drawable newDrawable(Resources res) {
+ return new AnimationDrawable(this, res);
+ }
+
+ public void addFrame(Drawable dr, int dur) {
+ // Do not combine the following. The array index must be evaluated before
+ // the array is accessed because super.addChild(dr) has a side effect on mDurations.
+ int pos = super.addChild(dr);
+ mDurations[pos] = dur;
+ }
+
+ @Override
+ public void growArray(int oldSize, int newSize) {
+ super.growArray(oldSize, newSize);
+ int[] newDurations = new int[newSize];
+ System.arraycopy(mDurations, 0, newDurations, 0, oldSize);
+ mDurations = newDurations;
+ }
+ }
+
+ private AnimationDrawable(AnimationState state, Resources res) {
+ AnimationState as = new AnimationState(state, this, res);
+ mAnimationState = as;
+ setConstantState(as);
+ if (state != null) {
+ setFrame(0, true, false);
+ }
+ }
}
diff --git a/src/api-impl/android/graphics/drawable/Drawable.java b/src/api-impl/android/graphics/drawable/Drawable.java
index d3c23639..8d4173af 100644
--- a/src/api-impl/android/graphics/drawable/Drawable.java
+++ b/src/api-impl/android/graphics/drawable/Drawable.java
@@ -22,12 +22,18 @@ import android.util.AttributeSet;
import android.util.TypedValue;
public class Drawable {
- public static interface Callback {}
+ public static interface Callback {
+ public void invalidateDrawable(Drawable drawable);
+ public void scheduleDrawable(Drawable drawable, Runnable runnable, long time);
+ public void unscheduleDrawable(Drawable drawable, Runnable runnable);
+ }
private Rect mBounds = new Rect();
private int[] mStateSet = new int[0];
public long paintable;
+ private Callback callback = null;
+
public Drawable() {
this.paintable = native_constructor();
}
@@ -75,9 +81,28 @@ public class Drawable {
return mStateSet;
}
- public void invalidateSelf() {}
+ public void setCallback(Callback callback) {
+ this.callback = callback;
+ }
- public void setCallback(Callback callback) {}
+ public void invalidateSelf() {
+ /* this shouldn't ever be needed with Gtk, but let's play it safe for now */
+ if (this.callback != null) {
+ callback.invalidateDrawable(this);
+ }
+ }
+
+ public void scheduleSelf(Runnable runnable, long time) {
+ if (this.callback != null) {
+ callback.scheduleDrawable(this, runnable, time);
+ }
+ }
+
+ public void unscheduleSelf(Runnable runnable) {
+ if (this.callback != null) {
+ callback.unscheduleDrawable(this, runnable);
+ }
+ }
public boolean isVisible() {
return false;
diff --git a/src/api-impl/android/graphics/drawable/DrawableContainer.java b/src/api-impl/android/graphics/drawable/DrawableContainer.java
index e48241cc..be36a85f 100644
--- a/src/api-impl/android/graphics/drawable/DrawableContainer.java
+++ b/src/api-impl/android/graphics/drawable/DrawableContainer.java
@@ -18,6 +18,7 @@ public class DrawableContainer extends Drawable {
if (idx >= 0 && idx < state.childCount && idx != curIndex && state.drawables[idx] != null) {
curIndex = idx;
native_selectChild(paintable, state.drawables[idx].paintable);
+ invalidateSelf();
return true;
}
return false;
@@ -34,7 +35,7 @@ public class DrawableContainer extends Drawable {
public DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, Resources res) {
}
-
+
public int getCapacity() {
return drawables.length;
}
@@ -43,6 +44,10 @@ public class DrawableContainer extends Drawable {
return childCount;
}
+ public Drawable getChild(int idx) {
+ return drawables[idx];
+ }
+
public int addChild(Drawable dr) {
if (childCount >= drawables.length) {
growArray(drawables.length, drawables.length * 2);
@@ -57,5 +62,5 @@ public class DrawableContainer extends Drawable {
drawables = newDrawables;
}
}
-
+
}
diff --git a/src/api-impl/android/graphics/drawable/GradientDrawable.java b/src/api-impl/android/graphics/drawable/GradientDrawable.java
index 35591194..c2e98911 100644
--- a/src/api-impl/android/graphics/drawable/GradientDrawable.java
+++ b/src/api-impl/android/graphics/drawable/GradientDrawable.java
@@ -7,5 +7,4 @@ public class GradientDrawable extends Drawable {
public void setCornerRadius(float cornerRadius) {}
public void setShape(int shape) {}
-
}
diff --git a/src/api-impl/android/view/View.java b/src/api-impl/android/view/View.java
index e3fdee64..a8477011 100644
--- a/src/api-impl/android/view/View.java
+++ b/src/api-impl/android/view/View.java
@@ -16,6 +16,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.LayoutDirection;
@@ -29,7 +30,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-public class View extends Object {
+public class View implements Drawable.Callback {
// --- constants from android source
@@ -1007,6 +1008,18 @@ public class View extends Object {
protected void onFinishInflate() {}
+ public void invalidateDrawable(Drawable drawable) {
+ nativeInvalidate(widget);
+ }
+
+ public void scheduleDrawable(Drawable drawable, Runnable runnable, long time) {
+ postDelayed(runnable, time - SystemClock.uptimeMillis());
+ }
+
+ public void unscheduleDrawable(Drawable drawable, Runnable runnable) {
+ /* TODO */
+ }
+
public void invalidate(Rect dirty) {
nativeInvalidate(widget);
}
@@ -1271,6 +1284,8 @@ public class View extends Object {
public void setBackgroundDrawable(Drawable backgroundDrawable) {
this.background = backgroundDrawable;
+ if(backgroundDrawable != null)
+ backgroundDrawable.setCallback(this);
native_setBackgroundDrawable(widget, backgroundDrawable != null ? backgroundDrawable.paintable : 0);
}
diff --git a/src/api-impl/android/widget/ImageView.java b/src/api-impl/android/widget/ImageView.java
index 1d9e229b..5c1ca0f6 100644
--- a/src/api-impl/android/widget/ImageView.java
+++ b/src/api-impl/android/widget/ImageView.java
@@ -73,6 +73,7 @@ public class ImageView extends View {
if (drawable instanceof BitmapDrawable) {
setImageBitmap(((BitmapDrawable) drawable).getBitmap());
} else if (drawable != null && drawable.paintable != 0) {
+ drawable.setCallback(this);
native_setDrawable(widget, drawable.paintable);
}
}