Drawable.java revision f4f7046183f0daedd17703fa16cf9026442bdaee
1/*
2 * Copyright (C) 2006 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
17package android.graphics.drawable;
18
19import java.io.InputStream;
20import java.io.IOException;
21import java.util.Arrays;
22
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25
26import android.content.res.Resources;
27import android.content.res.TypedArray;
28import android.graphics.*;
29import android.util.AttributeSet;
30import android.util.StateSet;
31import android.util.Xml;
32import android.util.TypedValue;
33
34/**
35 * A Drawable is a general abstraction for "something that can be drawn."  Most
36 * often you will deal with Drawable as the type of resource retrieved for
37 * drawing things to the screen; the Drawable class provides a generic API for
38 * dealing with an underlying visual resource that may take a variety of forms.
39 * Unlike a {@link android.view.View}, a Drawable does not have any facility to
40 * receive events or otherwise interact with the user.
41 *
42 * <p>In addition to simple drawing, Drawable provides a number of generic
43 * mechanisms for its client to interact with what is being drawn:
44 *
45 * <ul>
46 *     <li> The {@link #setBounds} method <var>must</var> be called to tell the
47 *     Drawable where it is drawn and how large it should be.  All Drawables
48 *     should respect the requested size, often simply by scaling their
49 *     imagery.  A client can find the preferred size for some Drawables with
50 *     the {@link #getIntrinsicHeight} and {@link #getIntrinsicWidth} methods.
51 *
52 *     <li> The {@link #getPadding} method can return from some Drawables
53 *     information about how to frame content that is placed inside of them.
54 *     For example, a Drawable that is intended to be the frame for a button
55 *     widget would need to return padding that correctly places the label
56 *     inside of itself.
57 *
58 *     <li> The {@link #setState} method allows the client to tell the Drawable
59 *     in which state it is to be drawn, such as "focused", "selected", etc.
60 *     Some drawables may modify their imagery based on the selected state.
61 *
62 *     <li> The {@link #setLevel} method allows the client to supply a single
63 *     continuous controller that can modify the Drawable is displayed, such as
64 *     a battery level or progress level.  Some drawables may modify their
65 *     imagery based on the current level.
66 *
67 *     <li> A Drawable can perform animations by calling back to its client
68 *     through the {@link Callback} interface.  All clients should support this
69 *     interface (via {@link #setCallback}) so that animations will work.  A
70 *     simple way to do this is through the system facilities such as
71 *     {@link android.view.View#setBackgroundDrawable(Drawable)} and
72 *     {@link android.widget.ImageView}.
73 * </ul>
74 *
75 * Though usually not visible to the application, Drawables may take a variety
76 * of forms:
77 *
78 * <ul>
79 *     <li> <b>Bitmap</b>: the simplest Drawable, a PNG or JPEG image.
80 *     <li> <b>Nine Patch</b>: an extension to the PNG format allows it to
81 *     specify information about how to stretch it and place things inside of
82 *     it.
83 *     <li> <b>Shape</b>: contains simple drawing commands instead of a raw
84 *     bitmap, allowing it to resize better in some cases.
85 *     <li> <b>Layers</b>: a compound drawable, which draws multiple underlying
86 *     drawables on top of each other.
87 *     <li> <b>States</b>: a compound drawable that selects one of a set of
88 *     drawables based on its state.
89 *     <li> <b>Levels</b>: a compound drawable that selects one of a set of
90 *     drawables based on its level.
91 *     <li> <b>Scale</b>: a compound drawable with a single child drawable,
92 *     whose overall size is modified based on the current level.
93 * </ul>
94 * <p>For information and examples of creating drawable resources (XML or bitmap files that
95 * can be loaded in code), see <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources
96 * and Internationalization</a>.
97 */
98public abstract class Drawable {
99    private static final Rect ZERO_BOUNDS_RECT = new Rect();
100
101    private int[] mStateSet = StateSet.WILD_CARD;
102    private int mLevel = 0;
103    private int mChangingConfigurations = 0;
104    private Rect mBounds = ZERO_BOUNDS_RECT;
105    /*package*/ Callback mCallback = null;
106    private boolean mVisible = true;
107
108    /**
109     * Draw in its bounds (set via setBounds) respecting optional effects such
110     * as alpha (set via setAlpha) and color filter (set via setColorFilter).
111     *
112     * @param canvas The canvas to draw into
113     */
114    public abstract void draw(Canvas canvas);
115
116    /**
117     * Specify a bounding rectangle for the Drawable. This is where the drawable
118     * will draw when its draw() method is called.
119     */
120    public void setBounds(int left, int top, int right, int bottom) {
121        Rect oldBounds = mBounds;
122
123        if (oldBounds == ZERO_BOUNDS_RECT) {
124            oldBounds = mBounds = new Rect();
125        }
126
127        if (oldBounds.left != left || oldBounds.top != top ||
128                oldBounds.right != right || oldBounds.bottom != bottom) {
129            mBounds.set(left, top, right, bottom);
130            onBoundsChange(mBounds);
131        }
132    }
133
134    /**
135     * Specify a bounding rectangle for the Drawable. This is where the drawable
136     * will draw when its draw() method is called.
137     */
138    public void setBounds(Rect bounds) {
139        setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
140    }
141
142    /**
143     * Return a copy of the drawable's bounds in the specified Rect (allocated
144     * by the caller). The bounds specify where this will draw when its draw()
145     * method is called.
146     *
147     * @param bounds Rect to receive the drawable's bounds (allocated by the
148     *               caller).
149     */
150    public final void copyBounds(Rect bounds) {
151        bounds.set(mBounds);
152    }
153
154    /**
155     * Return a copy of the drawable's bounds in a new Rect. This returns the
156     * same values as getBounds(), but the returned object is guaranteed to not
157     * be changed later by the drawable (i.e. it retains no reference to this
158     * rect). If the caller already has a Rect allocated, call copyBounds(rect).
159     *
160     * @return A copy of the drawable's bounds
161     */
162    public final Rect copyBounds() {
163        return new Rect(mBounds);
164    }
165
166    /**
167     * Return the drawable's bounds Rect. Note: for efficiency, the returned
168     * object may be the same object stored in the drawable (though this is not
169     * guaranteed), so if a persistent copy of the bounds is needed, call
170     * copyBounds(rect) instead.
171     * You should also not change the object returned by this method as it may
172     * be the same object stored in the drawable.
173     *
174     * @return The bounds of the drawable (which may change later, so caller
175     *         beware). DO NOT ALTER the returned object as it may change the
176     *         stored bounds of this drawable.
177     *
178     * @see #copyBounds()
179     * @see #copyBounds(android.graphics.Rect)
180     */
181    public final Rect getBounds() {
182        if (mBounds == ZERO_BOUNDS_RECT) {
183            mBounds = new Rect();
184        }
185
186        return mBounds;
187    }
188
189    /**
190     * Set a mask of the configuration parameters for which this drawable
191     * may change, requiring that it be re-created.
192     *
193     * @param configs A mask of the changing configuration parameters, as
194     * defined by {@link android.content.res.Configuration}.
195     *
196     * @see android.content.res.Configuration
197     */
198    public void setChangingConfigurations(int configs) {
199        mChangingConfigurations = configs;
200    }
201
202    /**
203     * Return a mask of the configuration parameters for which this drawable
204     * mau change, requiring that it be re-created.  The default implementation
205     * returns whatever was provided through
206     * {@link #setChangingConfigurations(int)} or 0 by default.  Subclasses
207     * may extend this to or in the changing configurations of any other
208     * drawables they hold.
209     *
210     * @return Returns a mask of the changing configuration parameters, as
211     * defined by {@link android.content.res.Configuration}.
212     *
213     * @see android.content.res.Configuration
214     */
215    public int getChangingConfigurations() {
216        return mChangingConfigurations;
217    }
218
219    /**
220     * Set to true to have the drawable dither its colors when drawn to a device
221     * with fewer than 8-bits per color component. This can improve the look on
222     * those devices, but can also slow down the drawing a little.
223     */
224    public void setDither(boolean dither) {}
225
226    /**
227     * Set to true to have the drawable filter its bitmap when scaled or rotated
228     * (for drawables that use bitmaps). If the drawable does not use bitmaps,
229     * this call is ignored. This can improve the look when scaled or rotated,
230     * but also slows down the drawing.
231     */
232    public void setFilterBitmap(boolean filter) {}
233
234    /**
235     * Implement this interface if you want to create an animated drawable that
236     * extends {@link android.graphics.drawable.Drawable Drawable}.
237     * Upon retrieving a drawable, use
238     * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
239     * to supply your implementation of the interface to the drawable; it uses
240     * this interface to schedule and execute animation changes.
241     */
242    public static interface Callback {
243        /**
244         * Called when the drawable needs to be redrawn.  A view at this point
245         * should invalidate itself (or at least the part of itself where the
246         * drawable appears).
247         *
248         * @param who The drawable that is requesting the update.
249         */
250        public void invalidateDrawable(Drawable who);
251
252        /**
253         * A Drawable can call this to schedule the next frame of its
254         * animation.  An implementation can generally simply call
255         * {@link android.os.Handler#postAtTime(Runnable, Object, long)} with
256         * the parameters <var>(what, who, when)</var> to perform the
257         * scheduling.
258         *
259         * @param who The drawable being scheduled.
260         * @param what The action to execute.
261         * @param when The time (in milliseconds) to run.  The timebase is
262         *             {@link android.os.SystemClock#uptimeMillis}
263         */
264        public void scheduleDrawable(Drawable who, Runnable what, long when);
265
266        /**
267         * A Drawable can call this to unschedule an action previously
268         * scheduled with {@link #scheduleDrawable}.  An implementation can
269         * generally simply call
270         * {@link android.os.Handler#removeCallbacks(Runnable, Object)} with
271         * the parameters <var>(what, who)</var> to unschedule the drawable.
272         *
273         * @param who The drawable being unscheduled.
274         * @param what The action being unscheduled.
275         */
276        public void unscheduleDrawable(Drawable who, Runnable what);
277    }
278
279    /**
280     * Bind a {@link Callback} object to this Drawable.  Required for clients
281     * that want to support animated drawables.
282     *
283     * @param cb The client's Callback implementation.
284     */
285    public final void setCallback(Callback cb) {
286        mCallback = cb;
287    }
288
289    /**
290     * Use the current {@link Callback} implementation to have this Drawable
291     * redrawn.  Does nothing if there is no Callback attached to the
292     * Drawable.
293     *
294     * @see Callback#invalidateDrawable
295     */
296    public void invalidateSelf()
297    {
298        if (mCallback != null) {
299            mCallback.invalidateDrawable(this);
300        }
301    }
302
303    /**
304     * Use the current {@link Callback} implementation to have this Drawable
305     * scheduled.  Does nothing if there is no Callback attached to the
306     * Drawable.
307     *
308     * @param what The action being scheduled.
309     * @param when The time (in milliseconds) to run.
310     *
311     * @see Callback#scheduleDrawable
312     */
313    public void scheduleSelf(Runnable what, long when)
314    {
315        if (mCallback != null) {
316            mCallback.scheduleDrawable(this, what, when);
317        }
318    }
319
320    /**
321     * Use the current {@link Callback} implementation to have this Drawable
322     * unscheduled.  Does nothing if there is no Callback attached to the
323     * Drawable.
324     *
325     * @param what The runnable that you no longer want called.
326     *
327     * @see Callback#unscheduleDrawable
328     */
329    public void unscheduleSelf(Runnable what)
330    {
331        if (mCallback != null) {
332            mCallback.unscheduleDrawable(this, what);
333        }
334    }
335
336    /**
337     * Specify an alpha value for the drawable. 0 means fully transparent, and
338     * 255 means fully opaque.
339     */
340    public abstract void setAlpha(int alpha);
341
342    /**
343     * Specify an optional colorFilter for the drawable. Pass null to remove
344     * any filters.
345    */
346    public abstract void setColorFilter(ColorFilter cf);
347
348    /**
349     * Specify a color and porterduff mode to be the colorfilter for this
350     * drawable.
351     */
352    public void setColorFilter(int color, PorterDuff.Mode mode) {
353        setColorFilter(new PorterDuffColorFilter(color, mode));
354    }
355
356    public void clearColorFilter() {
357        setColorFilter(null);
358    }
359
360    /**
361     * Indicates whether this view will change its appearance based on state.
362     * Clients can use this to determine whether it is necessary to calculate
363     * their state and call setState.
364     *
365     * @return True if this view changes its appearance based on state, false
366     *         otherwise.
367     *
368     * @see #setState(int[])
369     */
370    public boolean isStateful() {
371        return false;
372    }
373
374    /**
375     * Specify a set of states for the drawable. These are use-case specific,
376     * so see the relevant documentation. As an example, the background for
377     * widgets like Button understand the following states:
378     * [{@link android.R.attr#state_focused},
379     *  {@link android.R.attr#state_pressed}].
380     *
381     * <p>If the new state you are supplying causes the appearance of the
382     * Drawable to change, then it is responsible for calling
383     * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
384     * true will be returned from this function.
385     *
386     * <p>Note: The Drawable holds a reference on to <var>stateSet</var>
387     * until a new state array is given to it, so you must not modify this
388     * array during that time.</p>
389     *
390     * @param stateSet The new set of states to be displayed.
391     *
392     * @return Returns true if this change in state has caused the appearance
393     * of the Drawable to change (hence requiring an invalidate), otherwise
394     * returns false.
395     */
396    public boolean setState(final int[] stateSet) {
397        if (!Arrays.equals(mStateSet, stateSet)) {
398            mStateSet = stateSet;
399            return onStateChange(stateSet);
400        }
401        return false;
402    }
403
404    /**
405     * Describes the current state, as a union of primitve states, such as
406     * {@link android.R.attr#state_focused},
407     * {@link android.R.attr#state_selected}, etc.
408     * Some drawables may modify their imagery based on the selected state.
409     * @return An array of resource Ids describing the current state.
410     */
411    public int[] getState() {
412        return mStateSet;
413    }
414
415    /**
416     * @return The current drawable that will be used by this drawable. For simple drawables, this
417     *         is just the drawable itself. For drawables that change state like
418     *         {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
419     *         currently in use.
420     */
421    public Drawable getCurrent() {
422        return this;
423    }
424
425    /**
426     * Specify the level for the drawable.  This allows a drawable to vary its
427     * imagery based on a continuous controller, for example to show progress
428     * or volume level.
429     *
430     * <p>If the new level you are supplying causes the appearance of the
431     * Drawable to change, then it is responsible for calling
432     * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
433     * true will be returned from this function.
434     *
435     * @param level The new level, from 0 (minimum) to 10000 (maximum).
436     *
437     * @return Returns true if this change in level has caused the appearance
438     * of the Drawable to change (hence requiring an invalidate), otherwise
439     * returns false.
440     */
441    public final boolean setLevel(int level) {
442        if (mLevel != level) {
443            mLevel = level;
444            return onLevelChange(level);
445        }
446        return false;
447    }
448
449    /**
450     * Retrieve the current level.
451     *
452     * @return int Current level, from 0 (minimum) to 10000 (maximum).
453     */
454    public final int getLevel() {
455        return mLevel;
456    }
457
458    /**
459     * Set whether this Drawable is visible.  This generally does not impact
460     * the Drawable's behavior, but is a hint that can be used by some
461     * Drawables, for example, to decide whether run animations.
462     *
463     * @param visible Set to true if visible, false if not.
464     * @param restart You can supply true here to force the drawable to behave
465     *                as if it has just become visible, even if it had last
466     *                been set visible.  Used for example to force animations
467     *                to restart.
468     *
469     * @return boolean Returns true if the new visibility is different than
470     *         its previous state.
471     */
472    public boolean setVisible(boolean visible, boolean restart) {
473        boolean changed = mVisible != visible;
474        mVisible = visible;
475        return changed;
476    }
477
478    public final boolean isVisible() {
479        return mVisible;
480    }
481
482    /**
483     * Return the opacity/transparency of this Drawable.  The returned value is
484     * one of the abstract format constants in
485     * {@link android.graphics.PixelFormat}:
486     * {@link android.graphics.PixelFormat#UNKNOWN},
487     * {@link android.graphics.PixelFormat#TRANSLUCENT},
488     * {@link android.graphics.PixelFormat#TRANSPARENT}, or
489     * {@link android.graphics.PixelFormat#OPAQUE}.
490     *
491     * <p>Generally a Drawable should be as conservative as possible with the
492     * value it returns.  For example, if it contains multiple child drawables
493     * and only shows one of them at a time, if only one of the children is
494     * TRANSLUCENT and the others are OPAQUE then TRANSLUCENT should be
495     * returned.  You can use the method {@link #resolveOpacity} to perform a
496     * standard reduction of two opacities to the appropriate single output.
497     *
498     * <p>Note that the returned value does <em>not</em> take into account a
499     * custom alpha or color filter that has been applied by the client through
500     * the {@link #setAlpha} or {@link #setColorFilter} methods.
501     *
502     * @return int The opacity class of the Drawable.
503     *
504     * @see android.graphics.PixelFormat
505     */
506    public abstract int getOpacity();
507
508    /**
509     * Return the appropriate opacity value for two source opacities.  If
510     * either is UNKNOWN, that is returned; else, if either is TRANSLUCENT,
511     * that is returned; else, if either is TRANSPARENT, that is returned;
512     * else, OPAQUE is returned.
513     *
514     * <p>This is to help in implementing {@link #getOpacity}.
515     *
516     * @param op1 One opacity value.
517     * @param op2 Another opacity value.
518     *
519     * @return int The combined opacity value.
520     *
521     * @see #getOpacity
522     */
523    public static int resolveOpacity(int op1, int op2) {
524        if (op1 == op2) {
525            return op1;
526        }
527        if (op1 == PixelFormat.UNKNOWN || op2 == PixelFormat.UNKNOWN) {
528            return PixelFormat.UNKNOWN;
529        }
530        if (op1 == PixelFormat.TRANSLUCENT || op2 == PixelFormat.TRANSLUCENT) {
531            return PixelFormat.TRANSLUCENT;
532        }
533        if (op1 == PixelFormat.TRANSPARENT || op2 == PixelFormat.TRANSPARENT) {
534            return PixelFormat.TRANSPARENT;
535        }
536        return PixelFormat.OPAQUE;
537    }
538
539    /**
540     * Returns a Region representing the part of the Drawable that is completely
541     * transparent.  This can be used to perform drawing operations, identifying
542     * which parts of the target will not change when rendering the Drawable.
543     * The default implementation returns null, indicating no transparent
544     * region; subclasses can optionally override this to return an actual
545     * Region if they want to supply this optimization information, but it is
546     * not required that they do so.
547     *
548     * @return Returns null if the Drawables has no transparent region to
549     * report, else a Region holding the parts of the Drawable's bounds that
550     * are transparent.
551     */
552    public Region getTransparentRegion() {
553        return null;
554    }
555
556    /**
557     * Override this in your subclass to change appearance if you recognize the
558     * specified state.
559     *
560     * @return Returns true if the state change has caused the appearance of
561     * the Drawable to change (that is, it needs to be drawn), else false
562     * if it looks the same and there is no need to redraw it since its
563     * last state.
564     */
565    protected boolean onStateChange(int[] state) { return false; }
566    /** Override this in your subclass to change appearance if you vary based
567     *  on level.
568     * @return Returns true if the level change has caused the appearance of
569     * the Drawable to change (that is, it needs to be drawn), else false
570     * if it looks the same and there is no need to redraw it since its
571     * last level.
572     */
573    protected boolean onLevelChange(int level) { return false; }
574    /**
575     * Override this in your subclass to change appearance if you recognize the
576     * specified state.
577     */
578    protected void onBoundsChange(Rect bounds) {}
579
580    /**
581     * Return the intrinsic width of the underlying drawable object.  Returns
582     * -1 if it has no intrinsic width, such as with a solid color.
583     */
584    public int getIntrinsicWidth() {
585        return -1;
586    }
587
588    /**
589     * Return the intrinsic height of the underlying drawable object. Returns
590     * -1 if it has no intrinsic height, such as with a solid color.
591     */
592    public int getIntrinsicHeight() {
593        return -1;
594    }
595
596    /**
597     * Returns the minimum width suggested by this Drawable. If a View uses this
598     * Drawable as a background, it is suggested that the View use at least this
599     * value for its width. (There will be some scenarios where this will not be
600     * possible.) This value should INCLUDE any padding.
601     *
602     * @return The minimum width suggested by this Drawable. If this Drawable
603     *         doesn't have a suggested minimum width, 0 is returned.
604     */
605    public int getMinimumWidth() {
606        final int intrinsicWidth = getIntrinsicWidth();
607        return intrinsicWidth > 0 ? intrinsicWidth : 0;
608    }
609
610    /**
611     * Returns the minimum height suggested by this Drawable. If a View uses this
612     * Drawable as a background, it is suggested that the View use at least this
613     * value for its height. (There will be some scenarios where this will not be
614     * possible.) This value should INCLUDE any padding.
615     *
616     * @return The minimum height suggested by this Drawable. If this Drawable
617     *         doesn't have a suggested minimum height, 0 is returned.
618     */
619    public int getMinimumHeight() {
620        final int intrinsicHeight = getIntrinsicHeight();
621        return intrinsicHeight > 0 ? intrinsicHeight : 0;
622    }
623
624    /**
625     * Return in padding the insets suggested by this Drawable for placing
626     * content inside the drawable's bounds. Positive values move toward the
627     * center of the Drawable (set Rect.inset). Returns true if this drawable
628     * actually has a padding, else false. When false is returned, the padding
629     * is always set to 0.
630     */
631    public boolean getPadding(Rect padding) {
632        padding.set(0, 0, 0, 0);
633        return false;
634    }
635
636    /**
637     * Make this drawable mutable. This operation cannot be reversed. A mutable
638     * drawable is guaranteed to not share its state with any other drawable.
639     * This is especially useful when you need to modify properties of drawables
640     * loaded from resources. By default, all drawables instances loaded from
641     * the same resource share a common state; if you modify the state of one
642     * instance, all the other instances will receive the same modification.
643     *
644     * Calling this method on a mutable Drawable will have no effect.
645     *
646     * @return This drawable.
647     */
648    public Drawable mutate() {
649        return this;
650    }
651
652    /**
653     * Create a drawable from an inputstream
654     */
655    public static Drawable createFromStream(InputStream is, String srcName) {
656        return createFromResourceStream(null, null, is, srcName);
657    }
658
659    /**
660     * Create a drawable from an inputstream
661     *
662     * @hide pending API council approval
663     */
664    public static Drawable createFromResourceStream(Resources res, TypedValue value,
665            InputStream is, String srcName) {
666
667        if (is == null) {
668            return null;
669        }
670
671        /*  ugh. The decodeStream contract is that we have already allocated
672            the pad rect, but if the bitmap does not had a ninepatch chunk,
673            then the pad will be ignored. If we could change this to lazily
674            alloc/assign the rect, we could avoid the GC churn of making new
675            Rects only to drop them on the floor.
676        */
677        Rect pad = new Rect();
678        Bitmap  bm = BitmapFactory.decodeStream(res, value, is, pad, null);
679        if (bm != null) {
680            byte[] np = bm.getNinePatchChunk();
681            if (np == null || !NinePatch.isNinePatchChunk(np)) {
682                np = null;
683                pad = null;
684            }
685            return drawableFromBitmap(res, bm, np, pad, srcName);
686        }
687        return null;
688    }
689
690    /**
691     * Create a drawable from an XML document. For more information on how to
692     * create resources in XML, see
693     * <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources and
694     * Internationalization</a>.
695     */
696    public static Drawable createFromXml(Resources r, XmlPullParser parser)
697            throws XmlPullParserException, IOException {
698        AttributeSet attrs = Xml.asAttributeSet(parser);
699
700        int type;
701        while ((type=parser.next()) != XmlPullParser.START_TAG &&
702                type != XmlPullParser.END_DOCUMENT) {
703            // Empty loop
704        }
705
706        if (type != XmlPullParser.START_TAG) {
707            throw new XmlPullParserException("No start tag found");
708        }
709
710        Drawable drawable = createFromXmlInner(r, parser, attrs);
711
712        if (drawable == null) {
713            throw new RuntimeException("Unknown initial tag: " + parser.getName());
714        }
715
716        return drawable;
717    }
718
719    /**
720     * Create from inside an XML document.  Called on a parser positioned at
721     * a tag in an XML document, tries to create a Drawable from that tag.
722     * Returns null if the tag is not a valid drawable.
723     */
724    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
725    throws XmlPullParserException, IOException {
726        Drawable drawable;
727
728        final String name = parser.getName();
729
730        if (name.equals("selector")) {
731            drawable = new StateListDrawable();
732        } else if (name.equals("level-list")) {
733            drawable = new LevelListDrawable();
734        } else if (name.equals("layer-list")) {
735            drawable = new LayerDrawable();
736        } else if (name.equals("transition")) {
737            drawable = new TransitionDrawable();
738        } else if (name.equals("color")) {
739            drawable = new ColorDrawable();
740        } else if (name.equals("shape")) {
741            drawable = new GradientDrawable();
742        } else if (name.equals("scale")) {
743            drawable = new ScaleDrawable();
744        } else if (name.equals("clip")) {
745            drawable = new ClipDrawable();
746        } else if (name.equals("rotate")) {
747            drawable = new RotateDrawable();
748        } else if (name.equals("animated-rotate")) {
749            drawable = new AnimatedRotateDrawable();
750        } else if (name.equals("animation-list")) {
751            drawable = new AnimationDrawable();
752        } else if (name.equals("inset")) {
753            drawable = new InsetDrawable();
754        } else if (name.equals("bitmap")) {
755            drawable = new BitmapDrawable();
756            if (r != null) {
757               ((BitmapDrawable) drawable).setDensityScale(r.getDisplayMetrics());
758            }
759        } else if (name.equals("nine-patch")) {
760            drawable = new NinePatchDrawable();
761        } else {
762            throw new XmlPullParserException(parser.getPositionDescription() +
763                    ": invalid drawable tag " + name);
764        }
765
766        drawable.inflate(r, parser, attrs);
767        return drawable;
768    }
769
770
771    /**
772     * Create a drawable from file path name.
773     */
774    public static Drawable createFromPath(String pathName) {
775        if (pathName == null) {
776            return null;
777        }
778
779        Bitmap bm = BitmapFactory.decodeFile(pathName);
780        if (bm != null) {
781            return drawableFromBitmap(null, bm, null, null, pathName);
782        }
783
784        return null;
785    }
786
787    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
788            throws XmlPullParserException, IOException {
789
790        TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
791        inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
792        a.recycle();
793    }
794
795    void inflateWithAttributes(Resources r, XmlPullParser parser,
796            TypedArray attrs, int visibleAttr)
797            throws XmlPullParserException, IOException {
798
799        mVisible = attrs.getBoolean(visibleAttr, mVisible);
800    }
801
802    public static abstract class ConstantState {
803        public abstract Drawable newDrawable();
804        public abstract int getChangingConfigurations();
805    }
806
807    public ConstantState getConstantState() {
808        return null;
809    }
810
811    private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
812            Rect pad, String srcName) {
813
814        if (np != null) {
815            return new NinePatchDrawable(bm, np, pad, srcName);
816        }
817
818        final BitmapDrawable drawable = new BitmapDrawable(bm);
819        if (res != null) {
820            drawable.setDensityScale(res.getDisplayMetrics());
821        }
822
823        return drawable;
824    }
825}
826
827