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