LayerDrawable.java revision 9eaa604116ea5ee2eca81095d4d587fe680857a7
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 android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.res.ColorStateList;
22import android.content.res.Resources;
23import android.content.res.Resources.Theme;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.ColorFilter;
28import android.graphics.Outline;
29import android.graphics.PixelFormat;
30import android.graphics.PorterDuff.Mode;
31import android.graphics.Rect;
32import android.util.AttributeSet;
33import android.util.LayoutDirection;
34import android.view.Gravity;
35import android.view.View;
36
37import com.android.internal.R;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41
42import java.io.IOException;
43import java.util.Collection;
44
45/**
46 * A Drawable that manages an array of other Drawables. These are drawn in array
47 * order, so the element with the largest index will be drawn on top.
48 * <p>
49 * It can be defined in an XML file with the <code>&lt;layer-list></code> element.
50 * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
51 * <p>
52 * For more information, see the guide to
53 * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
54 *
55 * @attr ref android.R.styleable#LayerDrawable_paddingMode
56 * @attr ref android.R.styleable#LayerDrawableItem_left
57 * @attr ref android.R.styleable#LayerDrawableItem_top
58 * @attr ref android.R.styleable#LayerDrawableItem_right
59 * @attr ref android.R.styleable#LayerDrawableItem_bottom
60 * @attr ref android.R.styleable#LayerDrawableItem_start
61 * @attr ref android.R.styleable#LayerDrawableItem_end
62 * @attr ref android.R.styleable#LayerDrawableItem_width
63 * @attr ref android.R.styleable#LayerDrawableItem_height
64 * @attr ref android.R.styleable#LayerDrawableItem_gravity
65 * @attr ref android.R.styleable#LayerDrawableItem_drawable
66 * @attr ref android.R.styleable#LayerDrawableItem_id
67*/
68public class LayerDrawable extends Drawable implements Drawable.Callback {
69    /**
70     * Padding mode used to nest each layer inside the padding of the previous
71     * layer.
72     *
73     * @see #setPaddingMode(int)
74     */
75    public static final int PADDING_MODE_NEST = 0;
76
77    /**
78     * Padding mode used to stack each layer directly atop the previous layer.
79     *
80     * @see #setPaddingMode(int)
81     */
82    public static final int PADDING_MODE_STACK = 1;
83
84    /** Value used for undefined start and end insets. */
85    private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
86
87    LayerState mLayerState;
88
89    private int mOpacityOverride = PixelFormat.UNKNOWN;
90    private int[] mPaddingL;
91    private int[] mPaddingT;
92    private int[] mPaddingR;
93    private int[] mPaddingB;
94
95    private final Rect mTmpRect = new Rect();
96    private final Rect mTmpOutRect = new Rect();
97    private final Rect mTmpContainer = new Rect();
98    private Rect mHotspotBounds;
99    private boolean mMutated;
100
101    /**
102     * Creates a new layer drawable with the list of specified layers.
103     *
104     * @param layers a list of drawables to use as layers in this new drawable,
105     *               must be non-null
106     */
107    public LayerDrawable(@NonNull Drawable[] layers) {
108        this(layers, null);
109    }
110
111    /**
112     * Creates a new layer drawable with the specified list of layers and the
113     * specified constant state.
114     *
115     * @param layers The list of layers to add to this drawable.
116     * @param state The constant drawable state.
117     */
118    LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) {
119        this(state, null);
120
121        if (layers == null) {
122            throw new IllegalArgumentException("layers must be non-null");
123        }
124
125        final int length = layers.length;
126        final ChildDrawable[] r = new ChildDrawable[length];
127        for (int i = 0; i < length; i++) {
128            r[i] = new ChildDrawable();
129            r[i].mDrawable = layers[i];
130            layers[i].setCallback(this);
131            mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
132        }
133        mLayerState.mNum = length;
134        mLayerState.mChildren = r;
135
136        ensurePadding();
137    }
138
139    LayerDrawable() {
140        this((LayerState) null, null);
141    }
142
143    LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
144        mLayerState = createConstantState(state, res);
145        if (mLayerState.mNum > 0) {
146            ensurePadding();
147        }
148    }
149
150    LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
151        return new LayerState(state, this, res);
152    }
153
154    @Override
155    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
156            throws XmlPullParserException, IOException {
157        super.inflate(r, parser, attrs, theme);
158
159        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
160        updateStateFromTypedArray(a);
161        a.recycle();
162
163        inflateLayers(r, parser, attrs, theme);
164
165        ensurePadding();
166    }
167
168    /**
169     * Initializes the constant state from the values in the typed array.
170     */
171    private void updateStateFromTypedArray(TypedArray a) {
172        final LayerState state = mLayerState;
173
174        // Account for any configuration changes.
175        state.mChangingConfigurations |= a.getChangingConfigurations();
176
177        // Extract the theme attributes, if any.
178        state.mThemeAttrs = a.extractThemeAttrs();
179
180        mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
181
182        state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
183                state.mAutoMirrored);
184        state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
185                state.mPaddingMode);
186    }
187
188    /**
189     * Inflates child layers using the specified parser.
190     */
191    private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
192            throws XmlPullParserException, IOException {
193        final LayerState state = mLayerState;
194
195        final int innerDepth = parser.getDepth() + 1;
196        int type;
197        int depth;
198        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
199                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
200            if (type != XmlPullParser.START_TAG) {
201                continue;
202            }
203
204            if (depth > innerDepth || !parser.getName().equals("item")) {
205                continue;
206            }
207
208            final ChildDrawable layer = new ChildDrawable();
209            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
210            updateLayerFromTypedArray(layer, a);
211            a.recycle();
212
213            if (layer.mDrawable == null) {
214                while ((type = parser.next()) == XmlPullParser.TEXT) {
215                }
216                if (type != XmlPullParser.START_TAG) {
217                    throw new XmlPullParserException(parser.getPositionDescription()
218                            + ": <item> tag requires a 'drawable' attribute or "
219                            + "child tag defining a drawable");
220                }
221                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
222            }
223
224            if (layer.mDrawable != null) {
225                state.mChildrenChangingConfigurations |=
226                        layer.mDrawable.getChangingConfigurations();
227                layer.mDrawable.setCallback(this);
228            }
229
230            addLayer(layer);
231        }
232    }
233
234    private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
235        final LayerState state = mLayerState;
236
237        // Account for any configuration changes.
238        state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
239
240        // Extract the theme attributes, if any.
241        layer.mThemeAttrs = a.extractThemeAttrs();
242
243        layer.mInsetL = a.getDimensionPixelOffset(
244                R.styleable.LayerDrawableItem_left, layer.mInsetL);
245        layer.mInsetT = a.getDimensionPixelOffset(
246                R.styleable.LayerDrawableItem_top, layer.mInsetT);
247        layer.mInsetR = a.getDimensionPixelOffset(
248                R.styleable.LayerDrawableItem_right, layer.mInsetR);
249        layer.mInsetB = a.getDimensionPixelOffset(
250                R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
251        layer.mInsetS = a.getDimensionPixelOffset(
252                R.styleable.LayerDrawableItem_start, layer.mInsetS);
253        layer.mInsetE = a.getDimensionPixelOffset(
254                R.styleable.LayerDrawableItem_end, layer.mInsetE);
255        layer.mWidth = a.getDimensionPixelSize(
256                R.styleable.LayerDrawableItem_width, layer.mWidth);
257        layer.mHeight = a.getDimensionPixelSize(
258                R.styleable.LayerDrawableItem_height, layer.mHeight);
259        layer.mGravity = a.getInteger(
260                R.styleable.LayerDrawableItem_gravity, layer.mGravity);
261        layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
262
263        final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
264        if (dr != null) {
265            layer.mDrawable = dr;
266        }
267    }
268
269    @Override
270    public void applyTheme(Theme t) {
271        super.applyTheme(t);
272
273        final LayerState state = mLayerState;
274        if (state == null) {
275            return;
276        }
277
278        if (state.mThemeAttrs != null) {
279            final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
280            updateStateFromTypedArray(a);
281            a.recycle();
282        }
283
284        final ChildDrawable[] array = state.mChildren;
285        final int N = state.mNum;
286        for (int i = 0; i < N; i++) {
287            final ChildDrawable layer = array[i];
288            if (layer.mThemeAttrs != null) {
289                final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
290                        R.styleable.LayerDrawableItem);
291                updateLayerFromTypedArray(layer, a);
292                a.recycle();
293            }
294
295            final Drawable d = layer.mDrawable;
296            if (d.canApplyTheme()) {
297                d.applyTheme(t);
298
299                // Update cached mask of child changing configurations.
300                state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
301            }
302        }
303
304        ensurePadding();
305    }
306
307    @Override
308    public boolean canApplyTheme() {
309        return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
310    }
311
312    /**
313     * @hide
314     */
315    @Override
316    public boolean isProjected() {
317        if (super.isProjected()) {
318            return true;
319        }
320
321        final ChildDrawable[] layers = mLayerState.mChildren;
322        final int N = mLayerState.mNum;
323        for (int i = 0; i < N; i++) {
324            if (layers[i].mDrawable.isProjected()) {
325                return true;
326            }
327        }
328
329        return false;
330    }
331
332    /**
333     * Adds a new layer at the end of list of layers and returns its index.
334     *
335     * @param layer The layer to add.
336     * @return The index of the layer.
337     */
338    int addLayer(ChildDrawable layer) {
339        final LayerState st = mLayerState;
340        final int N = st.mChildren != null ? st.mChildren.length : 0;
341        final int i = st.mNum;
342        if (i >= N) {
343            final ChildDrawable[] nu = new ChildDrawable[N + 10];
344            if (i > 0) {
345                System.arraycopy(st.mChildren, 0, nu, 0, i);
346            }
347
348            st.mChildren = nu;
349        }
350
351        st.mChildren[i] = layer;
352        st.mNum++;
353        st.invalidateCache();
354        return i;
355    }
356
357    /**
358     * Add a new layer to this drawable. The new layer is identified by an id.
359     *
360     * @param dr The drawable to add as a layer.
361     * @param themeAttrs Theme attributes extracted from the layer.
362     * @param id The id of the new layer.
363     * @param left The left padding of the new layer.
364     * @param top The top padding of the new layer.
365     * @param right The right padding of the new layer.
366     * @param bottom The bottom padding of the new layer.
367     */
368    ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id,
369            int left, int top, int right, int bottom) {
370        final ChildDrawable childDrawable = createLayer(dr);
371        childDrawable.mId = id;
372        childDrawable.mThemeAttrs = themeAttrs;
373        childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
374        childDrawable.mInsetL = left;
375        childDrawable.mInsetT = top;
376        childDrawable.mInsetR = right;
377        childDrawable.mInsetB = bottom;
378
379        addLayer(childDrawable);
380
381        mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations();
382        dr.setCallback(this);
383
384        return childDrawable;
385    }
386
387    private ChildDrawable createLayer(Drawable dr) {
388        final ChildDrawable layer = new ChildDrawable();
389        layer.mDrawable = dr;
390        return layer;
391    }
392
393    /**
394     * Adds a new layer containing the specified {@code drawable} to the end of
395     * the layer list and returns its index.
396     *
397     * @param dr The drawable to add as a new layer.
398     * @return The index of the new layer.
399     */
400    public int addLayer(Drawable dr) {
401        final ChildDrawable layer = createLayer(dr);
402        final int index = addLayer(layer);
403        ensurePadding();
404        return index;
405    }
406
407    /**
408     * Looks for a layer with the given ID and returns its {@link Drawable}.
409     * <p>
410     * If multiple layers are found for the given ID, returns the
411     * {@link Drawable} for the matching layer at the highest index.
412     *
413     * @param id The layer ID to search for.
414     * @return The {@link Drawable} for the highest-indexed layer that has the
415     *         given ID, or null if not found.
416     */
417    public Drawable findDrawableByLayerId(int id) {
418        final ChildDrawable[] layers = mLayerState.mChildren;
419        for (int i = mLayerState.mNum - 1; i >= 0; i--) {
420            if (layers[i].mId == id) {
421                return layers[i].mDrawable;
422            }
423        }
424
425        return null;
426    }
427
428    /**
429     * Sets the ID of a layer.
430     *
431     * @param index The index of the layer to modify, must be in the range
432     *              {@code 0...getNumberOfLayers()-1}.
433     * @param id The id to assign to the layer.
434     *
435     * @see #getId(int)
436     * @attr ref android.R.styleable#LayerDrawableItem_id
437     */
438    public void setId(int index, int id) {
439        mLayerState.mChildren[index].mId = id;
440    }
441
442    /**
443     * Returns the ID of the specified layer.
444     *
445     * @param index The index of the layer, must be in the range
446     *              {@code 0...getNumberOfLayers()-1}.
447     * @return The id of the layer or {@link android.view.View#NO_ID} if the
448     *         layer has no id.
449     *
450     * @see #setId(int, int)
451     * @attr ref android.R.styleable#LayerDrawableItem_id
452     */
453    public int getId(int index) {
454        if (index >= mLayerState.mNum) {
455            throw new IndexOutOfBoundsException();
456        }
457        return mLayerState.mChildren[index].mId;
458    }
459
460    /**
461     * Returns the number of layers contained within this layer drawable.
462     *
463     * @return The number of layers.
464     */
465    public int getNumberOfLayers() {
466        return mLayerState.mNum;
467    }
468
469    /**
470     * Replaces the {@link Drawable} for the layer with the given id.
471     *
472     * @param id The layer ID to search for.
473     * @param drawable The replacement {@link Drawable}.
474     * @return Whether the {@link Drawable} was replaced (could return false if
475     *         the id was not found).
476     */
477    public boolean setDrawableByLayerId(int id, Drawable drawable) {
478        final int index = findIndexByLayerId(id);
479        if (index < 0) {
480            return false;
481        }
482
483        setDrawable(index, drawable);
484        return true;
485    }
486
487    /**
488     * Returns the layer with the specified {@code id}.
489     * <p>
490     * If multiple layers have the same ID, returns the layer with the lowest
491     * index.
492     *
493     * @param id The ID of the layer to return.
494     * @return The index of the layer with the specified ID.
495     */
496    public int findIndexByLayerId(int id) {
497        final ChildDrawable[] layers = mLayerState.mChildren;
498        final int N = mLayerState.mNum;
499        for (int i = 0; i < N; i++) {
500            final ChildDrawable childDrawable = layers[i];
501            if (childDrawable.mId == id) {
502                return i;
503            }
504        }
505
506        return -1;
507    }
508
509    /**
510     * Sets the drawable for the layer at the specified index.
511     *
512     * @param index The index of the layer to modify, must be in the range
513     *              {@code 0...getNumberOfLayers()-1}.
514     * @param drawable The drawable to set for the layer.
515     *
516     * @see #getDrawable(int)
517     * @attr ref android.R.styleable#LayerDrawableItem_drawable
518     */
519    public void setDrawable(int index, Drawable drawable) {
520        if (index >= mLayerState.mNum) {
521            throw new IndexOutOfBoundsException();
522        }
523
524        final ChildDrawable[] layers = mLayerState.mChildren;
525        final ChildDrawable childDrawable = layers[index];
526        if (childDrawable.mDrawable != null) {
527            if (drawable != null) {
528                final Rect bounds = childDrawable.mDrawable.getBounds();
529                drawable.setBounds(bounds);
530            }
531
532            childDrawable.mDrawable.setCallback(null);
533        }
534
535        if (drawable != null) {
536            drawable.setCallback(this);
537            drawable.setLayoutDirection(getLayoutDirection());
538            drawable.setLevel(getLevel());
539        }
540
541        childDrawable.mDrawable = drawable;
542        mLayerState.invalidateCache();
543    }
544
545    /**
546     * Returns the drawable for the layer at the specified index.
547     *
548     * @param index The index of the layer, must be in the range
549     *              {@code 0...getNumberOfLayers()-1}.
550     * @return The {@link Drawable} at the specified layer index.
551     *
552     * @see #setDrawable(int, Drawable)
553     * @attr ref android.R.styleable#LayerDrawableItem_drawable
554     */
555    public Drawable getDrawable(int index) {
556        if (index >= mLayerState.mNum) {
557            throw new IndexOutOfBoundsException();
558        }
559        return mLayerState.mChildren[index].mDrawable;
560    }
561
562    /**
563     * Sets an explicit size for the specified layer.
564     * <p>
565     * <strong>Note:</strong> Setting an explicit layer size changes the
566     * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
567     * for more information.
568     *
569     * @param index the index of the layer to adjust
570     * @param w width in pixels, or -1 to use the intrinsic width
571     * @param h height in pixels, or -1 to use the intrinsic height
572     * @see #getLayerWidth(int)
573     * @see #getLayerHeight(int)
574     * @attr ref android.R.styleable#LayerDrawableItem_width
575     * @attr ref android.R.styleable#LayerDrawableItem_height
576     */
577    public void setLayerSize(int index, int w, int h) {
578        final ChildDrawable childDrawable = mLayerState.mChildren[index];
579        childDrawable.mWidth = w;
580        childDrawable.mHeight = h;
581    }
582
583    /**
584     * @param index the index of the layer to adjust
585     * @param w width in pixels, or -1 to use the intrinsic width
586     * @attr ref android.R.styleable#LayerDrawableItem_width
587     */
588    public void setLayerWidth(int index, int w) {
589        final ChildDrawable childDrawable = mLayerState.mChildren[index];
590        childDrawable.mWidth = w;
591    }
592
593    /**
594     * @param index the index of the drawable to adjust
595     * @return the explicit width of the layer, or -1 if not specified
596     * @see #setLayerSize(int, int, int)
597     * @attr ref android.R.styleable#LayerDrawableItem_width
598     */
599    public int getLayerWidth(int index) {
600        final ChildDrawable childDrawable = mLayerState.mChildren[index];
601        return childDrawable.mWidth;
602    }
603
604    /**
605     * @param index the index of the layer to adjust
606     * @param h height in pixels, or -1 to use the intrinsic height
607     * @attr ref android.R.styleable#LayerDrawableItem_height
608     */
609    public void setLayerHeight(int index, int h) {
610        final ChildDrawable childDrawable = mLayerState.mChildren[index];
611        childDrawable.mHeight = h;
612    }
613
614    /**
615     * @param index the index of the drawable to adjust
616     * @return the explicit height of the layer, or -1 if not specified
617     * @see #setLayerSize(int, int, int)
618     * @attr ref android.R.styleable#LayerDrawableItem_height
619     */
620    public int getLayerHeight(int index) {
621        final ChildDrawable childDrawable = mLayerState.mChildren[index];
622        return childDrawable.mHeight;
623    }
624
625    /**
626     * Sets the gravity used to position or stretch the specified layer within
627     * its container. Gravity is applied after any layer insets (see
628     * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
629     * {@link #setPaddingMode(int)}).
630     * <p>
631     * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
632     * behavior depends on whether an explicit width or height has been set
633     * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
634     * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
635     * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
636     * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
637     *
638     * @param index the index of the drawable to adjust
639     * @param gravity the gravity to set for the layer
640     *
641     * @see #getLayerGravity(int)
642     * @attr ref android.R.styleable#LayerDrawableItem_gravity
643     */
644    public void setLayerGravity(int index, int gravity) {
645        final ChildDrawable childDrawable = mLayerState.mChildren[index];
646        childDrawable.mGravity = gravity;
647    }
648
649    /**
650     * @param index the index of the layer
651     * @return the gravity used to position or stretch the specified layer
652     *         within its container
653     *
654     * @see #setLayerGravity(int, int)
655     * @attr ref android.R.styleable#LayerDrawableItem_gravity
656     */
657    public int getLayerGravity(int index) {
658        final ChildDrawable childDrawable = mLayerState.mChildren[index];
659        return childDrawable.mGravity;
660    }
661
662    /**
663     * Specifies the insets in pixels for the drawable at the specified index.
664     *
665     * @param index the index of the drawable to adjust
666     * @param l number of pixels to add to the left bound
667     * @param t number of pixels to add to the top bound
668     * @param r number of pixels to subtract from the right bound
669     * @param b number of pixels to subtract from the bottom bound
670     *
671     * @attr ref android.R.styleable#LayerDrawableItem_left
672     * @attr ref android.R.styleable#LayerDrawableItem_top
673     * @attr ref android.R.styleable#LayerDrawableItem_right
674     * @attr ref android.R.styleable#LayerDrawableItem_bottom
675     */
676    public void setLayerInset(int index, int l, int t, int r, int b) {
677        setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
678    }
679
680    /**
681     * Specifies the relative insets in pixels for the drawable at the
682     * specified index.
683     *
684     * @param index the index of the layer to adjust
685     * @param s number of pixels to inset from the start bound
686     * @param t number of pixels to inset from the top bound
687     * @param e number of pixels to inset from the end bound
688     * @param b number of pixels to inset from the bottom bound
689     *
690     * @attr ref android.R.styleable#LayerDrawableItem_start
691     * @attr ref android.R.styleable#LayerDrawableItem_top
692     * @attr ref android.R.styleable#LayerDrawableItem_end
693     * @attr ref android.R.styleable#LayerDrawableItem_bottom
694     */
695    public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
696        setLayerInsetInternal(index, 0, t, 0, b, s, e);
697    }
698
699    /**
700     * @param index the index of the layer to adjust
701     * @param l number of pixels to inset from the left bound
702     * @attr ref android.R.styleable#LayerDrawableItem_left
703     */
704    public void setLayerInsetLeft(int index, int l) {
705        final ChildDrawable childDrawable = mLayerState.mChildren[index];
706        childDrawable.mInsetL = l;
707    }
708
709    /**
710     * @param index the index of the layer
711     * @return number of pixels to inset from the left bound
712     * @attr ref android.R.styleable#LayerDrawableItem_left
713     */
714    public int getLayerInsetLeft(int index) {
715        final ChildDrawable childDrawable = mLayerState.mChildren[index];
716        return childDrawable.mInsetL;
717    }
718
719    /**
720     * @param index the index of the layer to adjust
721     * @param r number of pixels to inset from the right bound
722     * @attr ref android.R.styleable#LayerDrawableItem_right
723     */
724    public void setLayerInsetRight(int index, int r) {
725        final ChildDrawable childDrawable = mLayerState.mChildren[index];
726        childDrawable.mInsetR = r;
727    }
728
729    /**
730     * @param index the index of the layer
731     * @return number of pixels to inset from the right bound
732     * @attr ref android.R.styleable#LayerDrawableItem_right
733     */
734    public int getLayerInsetRight(int index) {
735        final ChildDrawable childDrawable = mLayerState.mChildren[index];
736        return childDrawable.mInsetR;
737    }
738
739    /**
740     * @param index the index of the layer to adjust
741     * @param t number of pixels to inset from the top bound
742     * @attr ref android.R.styleable#LayerDrawableItem_top
743     */
744    public void setLayerInsetTop(int index, int t) {
745        final ChildDrawable childDrawable = mLayerState.mChildren[index];
746        childDrawable.mInsetT = t;
747    }
748
749    /**
750     * @param index the index of the layer
751     * @return number of pixels to inset from the top bound
752     * @attr ref android.R.styleable#LayerDrawableItem_top
753     */
754    public int getLayerInsetTop(int index) {
755        final ChildDrawable childDrawable = mLayerState.mChildren[index];
756        return childDrawable.mInsetT;
757    }
758
759    /**
760     * @param index the index of the layer to adjust
761     * @param b number of pixels to inset from the bottom bound
762     * @attr ref android.R.styleable#LayerDrawableItem_bottom
763     */
764    public void setLayerInsetBottom(int index, int b) {
765        final ChildDrawable childDrawable = mLayerState.mChildren[index];
766        childDrawable.mInsetB = b;
767    }
768
769    /**
770     * @param index the index of the layer
771     * @return number of pixels to inset from the bottom bound
772     * @attr ref android.R.styleable#LayerDrawableItem_bottom
773     */
774    public int getLayerInsetBottom(int index) {
775        final ChildDrawable childDrawable = mLayerState.mChildren[index];
776        return childDrawable.mInsetB;
777    }
778
779    /**
780     * @param index the index of the layer to adjust
781     * @param s number of pixels to inset from the start bound
782     * @attr ref android.R.styleable#LayerDrawableItem_start
783     */
784    public void setLayerInsetStart(int index, int s) {
785        final ChildDrawable childDrawable = mLayerState.mChildren[index];
786        childDrawable.mInsetS = s;
787    }
788
789    /**
790     * @param index the index of the layer
791     * @return number of pixels to inset from the start bound
792     * @attr ref android.R.styleable#LayerDrawableItem_start
793     */
794    public int getLayerInsetStart(int index) {
795        final ChildDrawable childDrawable = mLayerState.mChildren[index];
796        return childDrawable.mInsetS;
797    }
798
799    /**
800     * @param index the index of the layer to adjust
801     * @param e number of pixels to inset from the end bound
802     * @attr ref android.R.styleable#LayerDrawableItem_end
803     */
804    public void setLayerInsetEnd(int index, int e) {
805        final ChildDrawable childDrawable = mLayerState.mChildren[index];
806        childDrawable.mInsetE = e;
807    }
808
809    /**
810     * @param index the index of the layer
811     * @return number of pixels to inset from the end bound
812     * @attr ref android.R.styleable#LayerDrawableItem_end
813     */
814    public int getLayerInsetEnd(int index) {
815        final ChildDrawable childDrawable = mLayerState.mChildren[index];
816        return childDrawable.mInsetE;
817    }
818
819    private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
820        final ChildDrawable childDrawable = mLayerState.mChildren[index];
821        childDrawable.mInsetL = l;
822        childDrawable.mInsetT = t;
823        childDrawable.mInsetR = r;
824        childDrawable.mInsetB = b;
825        childDrawable.mInsetS = s;
826        childDrawable.mInsetE = e;
827    }
828
829    /**
830     * Specifies how layer padding should affect the bounds of subsequent
831     * layers. The default value is {@link #PADDING_MODE_NEST}.
832     *
833     * @param mode padding mode, one of:
834     *            <ul>
835     *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
836     *            padding of the previous layer
837     *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
838     *            atop the previous layer
839     *            </ul>
840     *
841     * @see #getPaddingMode()
842     * @attr ref android.R.styleable#LayerDrawable_paddingMode
843     */
844    public void setPaddingMode(int mode) {
845        if (mLayerState.mPaddingMode != mode) {
846            mLayerState.mPaddingMode = mode;
847        }
848    }
849
850    /**
851     * @return the current padding mode
852     *
853     * @see #setPaddingMode(int)
854     * @attr ref android.R.styleable#LayerDrawable_paddingMode
855     */
856    public int getPaddingMode() {
857      return mLayerState.mPaddingMode;
858    }
859
860    @Override
861    public void invalidateDrawable(Drawable who) {
862        invalidateSelf();
863    }
864
865    @Override
866    public void scheduleDrawable(Drawable who, Runnable what, long when) {
867        scheduleSelf(what, when);
868    }
869
870    @Override
871    public void unscheduleDrawable(Drawable who, Runnable what) {
872        unscheduleSelf(what);
873    }
874
875    @Override
876    public void draw(Canvas canvas) {
877        final ChildDrawable[] array = mLayerState.mChildren;
878        final int N = mLayerState.mNum;
879        for (int i = 0; i < N; i++) {
880            array[i].mDrawable.draw(canvas);
881        }
882    }
883
884    @Override
885    public int getChangingConfigurations() {
886        return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
887    }
888
889    @Override
890    public boolean getPadding(Rect padding) {
891        if (mLayerState.mPaddingMode == PADDING_MODE_NEST) {
892            computeNestedPadding(padding);
893        } else {
894            computeStackedPadding(padding);
895        }
896
897        return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
898    }
899
900    private void computeNestedPadding(Rect padding) {
901        padding.left = 0;
902        padding.top = 0;
903        padding.right = 0;
904        padding.bottom = 0;
905
906        // Add all the padding.
907        final ChildDrawable[] array = mLayerState.mChildren;
908        final int N = mLayerState.mNum;
909        for (int i = 0; i < N; i++) {
910            refreshChildPadding(i, array[i]);
911
912            padding.left += mPaddingL[i];
913            padding.top += mPaddingT[i];
914            padding.right += mPaddingR[i];
915            padding.bottom += mPaddingB[i];
916        }
917    }
918
919    private void computeStackedPadding(Rect padding) {
920        padding.left = 0;
921        padding.top = 0;
922        padding.right = 0;
923        padding.bottom = 0;
924
925        // Take the max padding.
926        final ChildDrawable[] array = mLayerState.mChildren;
927        final int N = mLayerState.mNum;
928        for (int i = 0; i < N; i++) {
929            refreshChildPadding(i, array[i]);
930
931            padding.left = Math.max(padding.left, mPaddingL[i]);
932            padding.top = Math.max(padding.top, mPaddingT[i]);
933            padding.right = Math.max(padding.right, mPaddingR[i]);
934            padding.bottom = Math.max(padding.bottom, mPaddingB[i]);
935        }
936    }
937
938    /**
939     * Populates <code>outline</code> with the first available (non-empty) layer outline.
940     *
941     * @param outline Outline in which to place the first available layer outline
942     */
943    @Override
944    public void getOutline(@NonNull Outline outline) {
945        final LayerState state = mLayerState;
946        final ChildDrawable[] children = state.mChildren;
947        final int N = state.mNum;
948        for (int i = 0; i < N; i++) {
949            children[i].mDrawable.getOutline(outline);
950            if (!outline.isEmpty()) {
951                return;
952            }
953        }
954    }
955
956    @Override
957    public void setHotspot(float x, float y) {
958        final ChildDrawable[] array = mLayerState.mChildren;
959        final int N = mLayerState.mNum;
960        for (int i = 0; i < N; i++) {
961            array[i].mDrawable.setHotspot(x, y);
962        }
963    }
964
965    @Override
966    public void setHotspotBounds(int left, int top, int right, int bottom) {
967        final ChildDrawable[] array = mLayerState.mChildren;
968        final int N = mLayerState.mNum;
969        for (int i = 0; i < N; i++) {
970            array[i].mDrawable.setHotspotBounds(left, top, right, bottom);
971        }
972
973        if (mHotspotBounds == null) {
974            mHotspotBounds = new Rect(left, top, right, bottom);
975        } else {
976            mHotspotBounds.set(left, top, right, bottom);
977        }
978    }
979
980    @Override
981    public void getHotspotBounds(Rect outRect) {
982        if (mHotspotBounds != null) {
983            outRect.set(mHotspotBounds);
984        } else {
985            super.getHotspotBounds(outRect);
986        }
987    }
988
989    @Override
990    public boolean setVisible(boolean visible, boolean restart) {
991        final boolean changed = super.setVisible(visible, restart);
992        final ChildDrawable[] array = mLayerState.mChildren;
993        final int N = mLayerState.mNum;
994        for (int i = 0; i < N; i++) {
995            array[i].mDrawable.setVisible(visible, restart);
996        }
997
998        return changed;
999    }
1000
1001    @Override
1002    public void setDither(boolean dither) {
1003        final ChildDrawable[] array = mLayerState.mChildren;
1004        final int N = mLayerState.mNum;
1005        for (int i = 0; i < N; i++) {
1006            array[i].mDrawable.setDither(dither);
1007        }
1008    }
1009
1010    @Override
1011    public boolean getDither() {
1012        final ChildDrawable[] array = mLayerState.mChildren;
1013        if (mLayerState.mNum > 0) {
1014            // All layers should have the same dither set on them - just return
1015            // the first one
1016            return array[0].mDrawable.getDither();
1017        } else {
1018            return super.getDither();
1019        }
1020    }
1021
1022    @Override
1023    public void setAlpha(int alpha) {
1024        final ChildDrawable[] array = mLayerState.mChildren;
1025        final int N = mLayerState.mNum;
1026        for (int i = 0; i < N; i++) {
1027            array[i].mDrawable.setAlpha(alpha);
1028        }
1029    }
1030
1031    @Override
1032    public int getAlpha() {
1033        final ChildDrawable[] array = mLayerState.mChildren;
1034        if (mLayerState.mNum > 0) {
1035            // All layers should have the same alpha set on them - just return
1036            // the first one
1037            return array[0].mDrawable.getAlpha();
1038        } else {
1039            return super.getAlpha();
1040        }
1041    }
1042
1043    @Override
1044    public void setColorFilter(ColorFilter colorFilter) {
1045        final ChildDrawable[] array = mLayerState.mChildren;
1046        final int N = mLayerState.mNum;
1047        for (int i = 0; i < N; i++) {
1048            array[i].mDrawable.setColorFilter(colorFilter);
1049        }
1050    }
1051
1052    @Override
1053    public void setTintList(ColorStateList tint) {
1054        final ChildDrawable[] array = mLayerState.mChildren;
1055        final int N = mLayerState.mNum;
1056        for (int i = 0; i < N; i++) {
1057            array[i].mDrawable.setTintList(tint);
1058        }
1059    }
1060
1061    @Override
1062    public void setTintMode(Mode tintMode) {
1063        final ChildDrawable[] array = mLayerState.mChildren;
1064        final int N = mLayerState.mNum;
1065        for (int i = 0; i < N; i++) {
1066            array[i].mDrawable.setTintMode(tintMode);
1067        }
1068    }
1069
1070    /**
1071     * Sets the opacity of this drawable directly, instead of collecting the
1072     * states from the layers
1073     *
1074     * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
1075     *            PixelFormat.UNKNOWN} for the default behavior
1076     * @see PixelFormat#UNKNOWN
1077     * @see PixelFormat#TRANSLUCENT
1078     * @see PixelFormat#TRANSPARENT
1079     * @see PixelFormat#OPAQUE
1080     */
1081    public void setOpacity(int opacity) {
1082        mOpacityOverride = opacity;
1083    }
1084
1085    @Override
1086    public int getOpacity() {
1087        if (mOpacityOverride != PixelFormat.UNKNOWN) {
1088            return mOpacityOverride;
1089        }
1090        return mLayerState.getOpacity();
1091    }
1092
1093    @Override
1094    public void setAutoMirrored(boolean mirrored) {
1095        mLayerState.mAutoMirrored = mirrored;
1096
1097        final ChildDrawable[] array = mLayerState.mChildren;
1098        final int N = mLayerState.mNum;
1099        for (int i = 0; i < N; i++) {
1100            array[i].mDrawable.setAutoMirrored(mirrored);
1101        }
1102    }
1103
1104    @Override
1105    public boolean isAutoMirrored() {
1106        return mLayerState.mAutoMirrored;
1107    }
1108
1109    @Override
1110    public boolean isStateful() {
1111        return mLayerState.isStateful();
1112    }
1113
1114    @Override
1115    protected boolean onStateChange(int[] state) {
1116        boolean changed = false;
1117
1118        final ChildDrawable[] array = mLayerState.mChildren;
1119        final int N = mLayerState.mNum;
1120        for (int i = 0; i < N; i++) {
1121            final ChildDrawable r = array[i];
1122            if (r.mDrawable.isStateful() && r.mDrawable.setState(state)) {
1123                refreshChildPadding(i, r);
1124                changed = true;
1125            }
1126        }
1127
1128        if (changed) {
1129            updateLayerBounds(getBounds());
1130        }
1131
1132        return changed;
1133    }
1134
1135    @Override
1136    protected boolean onLevelChange(int level) {
1137        boolean changed = false;
1138
1139        final ChildDrawable[] array = mLayerState.mChildren;
1140        final int N = mLayerState.mNum;
1141        for (int i = 0; i < N; i++) {
1142            final ChildDrawable r = array[i];
1143            if (r.mDrawable.setLevel(level)) {
1144                refreshChildPadding(i, r);
1145                changed = true;
1146            }
1147        }
1148
1149        if (changed) {
1150            updateLayerBounds(getBounds());
1151        }
1152
1153        return changed;
1154    }
1155
1156    @Override
1157    protected void onBoundsChange(Rect bounds) {
1158        updateLayerBounds(bounds);
1159    }
1160
1161    private void updateLayerBounds(Rect bounds) {
1162        int padL = 0;
1163        int padT = 0;
1164        int padR = 0;
1165        int padB = 0;
1166
1167        final Rect outRect = mTmpOutRect;
1168        final int layoutDirection = getLayoutDirection();
1169        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1170        final ChildDrawable[] array = mLayerState.mChildren;
1171        final int N = mLayerState.mNum;
1172        for (int i = 0; i < N; i++) {
1173            final ChildDrawable r = array[i];
1174            final Drawable d = r.mDrawable;
1175            final Rect container = mTmpContainer;
1176            container.set(d.getBounds());
1177
1178            // Take the resolved layout direction into account. If start / end
1179            // padding are defined, they will be resolved (hence overriding) to
1180            // left / right or right / left depending on the resolved layout
1181            // direction. If start / end padding are not defined, use the
1182            // left / right ones.
1183            final int insetL, insetR;
1184            if (layoutDirection == LayoutDirection.RTL) {
1185                insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
1186                insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
1187            } else {
1188                insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
1189                insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
1190            }
1191
1192            // Establish containing region based on aggregate padding and
1193            // requested insets for the current layer.
1194            container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
1195                    bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
1196
1197            // Apply resolved gravity to drawable based on resolved size.
1198            final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight);
1199            final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
1200            final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
1201            Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
1202            d.setBounds(outRect);
1203
1204            if (nest) {
1205                padL += mPaddingL[i];
1206                padR += mPaddingR[i];
1207                padT += mPaddingT[i];
1208                padB += mPaddingB[i];
1209            }
1210        }
1211    }
1212
1213    /**
1214     * Resolves layer gravity given explicit gravity and dimensions.
1215     * <p>
1216     * If the client hasn't specified a gravity but has specified an explicit
1217     * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
1218     * preserve legacy behavior.
1219     *
1220     * @param gravity
1221     * @param width
1222     * @param height
1223     * @return
1224     */
1225    private int resolveGravity(int gravity, int width, int height) {
1226        if (!Gravity.isHorizontal(gravity)) {
1227            if (width < 0) {
1228                gravity |= Gravity.FILL_HORIZONTAL;
1229            } else {
1230                gravity |= Gravity.START;
1231            }
1232        }
1233
1234        if (!Gravity.isVertical(gravity)) {
1235            if (height < 0) {
1236                gravity |= Gravity.FILL_VERTICAL;
1237            } else {
1238                gravity |= Gravity.TOP;
1239            }
1240        }
1241
1242        return gravity;
1243    }
1244
1245    @Override
1246    public int getIntrinsicWidth() {
1247        int width = -1;
1248        int padL = 0;
1249        int padR = 0;
1250
1251        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1252        final ChildDrawable[] array = mLayerState.mChildren;
1253        final int N = mLayerState.mNum;
1254        for (int i = 0; i < N; i++) {
1255            final ChildDrawable r = array[i];
1256            final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
1257            final int w = minWidth + r.mInsetL + r.mInsetR + padL + padR;
1258            if (w > width) {
1259                width = w;
1260            }
1261
1262            if (nest) {
1263                padL += mPaddingL[i];
1264                padR += mPaddingR[i];
1265            }
1266        }
1267
1268        return width;
1269    }
1270
1271    @Override
1272    public int getIntrinsicHeight() {
1273        int height = -1;
1274        int padT = 0;
1275        int padB = 0;
1276
1277        final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1278        final ChildDrawable[] array = mLayerState.mChildren;
1279        final int N = mLayerState.mNum;
1280        for (int i = 0; i < N; i++) {
1281            final ChildDrawable r = array[i];
1282            final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
1283            final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
1284            if (h > height) {
1285                height = h;
1286            }
1287
1288            if (nest) {
1289                padT += mPaddingT[i];
1290                padB += mPaddingB[i];
1291            }
1292        }
1293
1294        return height;
1295    }
1296
1297    /**
1298     * Refreshes the cached padding values for the specified child.
1299     *
1300     * @return true if the child's padding has changed
1301     */
1302    private boolean refreshChildPadding(int i, ChildDrawable r) {
1303        final Rect rect = mTmpRect;
1304        r.mDrawable.getPadding(rect);
1305        if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
1306                rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
1307            mPaddingL[i] = rect.left;
1308            mPaddingT[i] = rect.top;
1309            mPaddingR[i] = rect.right;
1310            mPaddingB[i] = rect.bottom;
1311            return true;
1312        }
1313        return false;
1314    }
1315
1316    /**
1317     * Ensures the child padding caches are large enough.
1318     */
1319    void ensurePadding() {
1320        final int N = mLayerState.mNum;
1321        if (mPaddingL != null && mPaddingL.length >= N) {
1322            return;
1323        }
1324
1325        mPaddingL = new int[N];
1326        mPaddingT = new int[N];
1327        mPaddingR = new int[N];
1328        mPaddingB = new int[N];
1329    }
1330
1331    @Override
1332    public ConstantState getConstantState() {
1333        if (mLayerState.canConstantState()) {
1334            mLayerState.mChangingConfigurations = getChangingConfigurations();
1335            return mLayerState;
1336        }
1337        return null;
1338    }
1339
1340    @Override
1341    public Drawable mutate() {
1342        if (!mMutated && super.mutate() == this) {
1343            mLayerState = createConstantState(mLayerState, null);
1344            final ChildDrawable[] array = mLayerState.mChildren;
1345            final int N = mLayerState.mNum;
1346            for (int i = 0; i < N; i++) {
1347                array[i].mDrawable.mutate();
1348            }
1349            mMutated = true;
1350        }
1351        return this;
1352    }
1353
1354    /**
1355     * @hide
1356     */
1357    public void clearMutated() {
1358        super.clearMutated();
1359        final ChildDrawable[] array = mLayerState.mChildren;
1360        final int N = mLayerState.mNum;
1361        for (int i = 0; i < N; i++) {
1362            array[i].mDrawable.clearMutated();
1363        }
1364        mMutated = false;
1365    }
1366
1367    @Override
1368    public boolean onLayoutDirectionChange(int layoutDirection) {
1369        boolean changed = false;
1370        final ChildDrawable[] array = mLayerState.mChildren;
1371        final int N = mLayerState.mNum;
1372        for (int i = 0; i < N; i++) {
1373            changed |= array[i].mDrawable.setLayoutDirection(layoutDirection);
1374        }
1375        updateLayerBounds(getBounds());
1376        return changed;
1377    }
1378
1379    static class ChildDrawable {
1380        public Drawable mDrawable;
1381        public int[] mThemeAttrs;
1382        public int mInsetL, mInsetT, mInsetR, mInsetB;
1383        public int mInsetS = UNDEFINED_INSET;
1384        public int mInsetE = UNDEFINED_INSET;
1385        public int mWidth = -1;
1386        public int mHeight = -1;
1387        public int mGravity = Gravity.NO_GRAVITY;
1388        public int mId = View.NO_ID;
1389
1390        ChildDrawable() {
1391            // Default empty constructor.
1392        }
1393
1394        ChildDrawable(ChildDrawable orig, LayerDrawable owner, Resources res) {
1395            if (res != null) {
1396                mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
1397            } else {
1398                mDrawable = orig.mDrawable.getConstantState().newDrawable();
1399            }
1400            mDrawable.setCallback(owner);
1401            mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
1402            mDrawable.setBounds(orig.mDrawable.getBounds());
1403            mDrawable.setLevel(orig.mDrawable.getLevel());
1404            mThemeAttrs = orig.mThemeAttrs;
1405            mInsetL = orig.mInsetL;
1406            mInsetT = orig.mInsetT;
1407            mInsetR = orig.mInsetR;
1408            mInsetB = orig.mInsetB;
1409            mInsetS = orig.mInsetS;
1410            mInsetE = orig.mInsetE;
1411            mWidth = orig.mWidth;
1412            mHeight = orig.mHeight;
1413            mGravity = orig.mGravity;
1414            mId = orig.mId;
1415        }
1416    }
1417
1418    static class LayerState extends ConstantState {
1419        int mNum;
1420        ChildDrawable[] mChildren;
1421        int[] mThemeAttrs;
1422
1423        int mChangingConfigurations;
1424        int mChildrenChangingConfigurations;
1425
1426        private boolean mHaveOpacity;
1427        private int mOpacity;
1428
1429        private boolean mHaveIsStateful;
1430        private boolean mIsStateful;
1431
1432        private boolean mAutoMirrored = false;
1433
1434        private int mPaddingMode = PADDING_MODE_NEST;
1435
1436        LayerState(LayerState orig, LayerDrawable owner, Resources res) {
1437            if (orig != null) {
1438                final ChildDrawable[] origChildDrawable = orig.mChildren;
1439                final int N = orig.mNum;
1440
1441                mNum = N;
1442                mChildren = new ChildDrawable[N];
1443
1444                mChangingConfigurations = orig.mChangingConfigurations;
1445                mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
1446
1447                for (int i = 0; i < N; i++) {
1448                    final ChildDrawable or = origChildDrawable[i];
1449                    mChildren[i] = new ChildDrawable(or, owner, res);
1450                }
1451
1452                mHaveOpacity = orig.mHaveOpacity;
1453                mOpacity = orig.mOpacity;
1454                mHaveIsStateful = orig.mHaveIsStateful;
1455                mIsStateful = orig.mIsStateful;
1456                mAutoMirrored = orig.mAutoMirrored;
1457                mPaddingMode = orig.mPaddingMode;
1458                mThemeAttrs = orig.mThemeAttrs;
1459            } else {
1460                mNum = 0;
1461                mChildren = null;
1462            }
1463        }
1464
1465        @Override
1466        public boolean canApplyTheme() {
1467            if (mThemeAttrs != null || super.canApplyTheme()) {
1468                return true;
1469            }
1470
1471            final ChildDrawable[] array = mChildren;
1472            final int N = mNum;
1473            for (int i = 0; i < N; i++) {
1474                final ChildDrawable layer = array[i];
1475                if (layer.mThemeAttrs != null || layer.mDrawable.canApplyTheme()) {
1476                    return true;
1477                }
1478            }
1479
1480            return false;
1481        }
1482
1483        @Override
1484        public Drawable newDrawable() {
1485            return new LayerDrawable(this, null);
1486        }
1487
1488        @Override
1489        public Drawable newDrawable(Resources res) {
1490            return new LayerDrawable(this, res);
1491        }
1492
1493        @Override
1494        public int getChangingConfigurations() {
1495            return mChangingConfigurations
1496                    | mChildrenChangingConfigurations;
1497        }
1498
1499        public final int getOpacity() {
1500            if (mHaveOpacity) {
1501                return mOpacity;
1502            }
1503
1504            final ChildDrawable[] array = mChildren;
1505            final int N = mNum;
1506            int op = N > 0 ? array[0].mDrawable.getOpacity() : PixelFormat.TRANSPARENT;
1507            for (int i = 1; i < N; i++) {
1508                op = Drawable.resolveOpacity(op, array[i].mDrawable.getOpacity());
1509            }
1510
1511            mOpacity = op;
1512            mHaveOpacity = true;
1513            return op;
1514        }
1515
1516        public final boolean isStateful() {
1517            if (mHaveIsStateful) {
1518                return mIsStateful;
1519            }
1520
1521            final ChildDrawable[] array = mChildren;
1522            final int N = mNum;
1523            boolean isStateful = false;
1524            for (int i = 0; i < N; i++) {
1525                if (array[i].mDrawable.isStateful()) {
1526                    isStateful = true;
1527                    break;
1528                }
1529            }
1530
1531            mIsStateful = isStateful;
1532            mHaveIsStateful = true;
1533            return isStateful;
1534        }
1535
1536        public final boolean canConstantState() {
1537            final ChildDrawable[] array = mChildren;
1538            final int N = mNum;
1539            for (int i = 0; i < N; i++) {
1540                if (array[i].mDrawable.getConstantState() == null) {
1541                    return false;
1542                }
1543            }
1544
1545            // Don't cache the result, this method is not called very often.
1546            return true;
1547        }
1548
1549        public void invalidateCache() {
1550            mHaveOpacity = false;
1551            mHaveIsStateful = false;
1552        }
1553
1554        @Override
1555        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
1556            final ChildDrawable[] array = mChildren;
1557            final int N = mNum;
1558            int pixelCount = 0;
1559            for (int i = 0; i < N; i++) {
1560                final ConstantState state = array[i].mDrawable.getConstantState();
1561                if (state != null) {
1562                    pixelCount += state.addAtlasableBitmaps(atlasList);
1563                }
1564            }
1565            return pixelCount;
1566        }
1567    }
1568}
1569
1570