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 
17 package android.graphics.drawable;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.res.ColorStateList;
22 import android.content.res.Resources;
23 import android.content.res.Resources.Theme;
24 import android.content.res.TypedArray;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.ColorFilter;
28 import android.graphics.Outline;
29 import android.graphics.PixelFormat;
30 import android.graphics.PorterDuff.Mode;
31 import android.graphics.Rect;
32 import android.util.AttributeSet;
33 import android.util.LayoutDirection;
34 import android.view.Gravity;
35 import android.view.View;
36 
37 import com.android.internal.R;
38 
39 import org.xmlpull.v1.XmlPullParser;
40 import org.xmlpull.v1.XmlPullParserException;
41 
42 import java.io.IOException;
43 import 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 */
68 public 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[] mPaddingL;
90     private int[] mPaddingT;
91     private int[] mPaddingR;
92     private int[] mPaddingB;
93 
94     private final Rect mTmpRect = new Rect();
95     private final Rect mTmpOutRect = new Rect();
96     private final Rect mTmpContainer = new Rect();
97     private Rect mHotspotBounds;
98     private boolean mMutated;
99 
100     /**
101      * Creates a new layer drawable with the list of specified layers.
102      *
103      * @param layers a list of drawables to use as layers in this new drawable,
104      *               must be non-null
105      */
LayerDrawable(@onNull Drawable[] layers)106     public LayerDrawable(@NonNull Drawable[] layers) {
107         this(layers, null);
108     }
109 
110     /**
111      * Creates a new layer drawable with the specified list of layers and the
112      * specified constant state.
113      *
114      * @param layers The list of layers to add to this drawable.
115      * @param state The constant drawable state.
116      */
LayerDrawable(@onNull Drawable[] layers, @Nullable LayerState state)117     LayerDrawable(@NonNull Drawable[] layers, @Nullable LayerState state) {
118         this(state, null);
119 
120         if (layers == null) {
121             throw new IllegalArgumentException("layers must be non-null");
122         }
123 
124         final int length = layers.length;
125         final ChildDrawable[] r = new ChildDrawable[length];
126         for (int i = 0; i < length; i++) {
127             r[i] = new ChildDrawable();
128             r[i].mDrawable = layers[i];
129             layers[i].setCallback(this);
130             mLayerState.mChildrenChangingConfigurations |= layers[i].getChangingConfigurations();
131         }
132         mLayerState.mNum = length;
133         mLayerState.mChildren = r;
134 
135         ensurePadding();
136         refreshPadding();
137     }
138 
LayerDrawable()139     LayerDrawable() {
140         this((LayerState) null, null);
141     }
142 
LayerDrawable(@ullable LayerState state, @Nullable Resources res)143     LayerDrawable(@Nullable LayerState state, @Nullable Resources res) {
144         mLayerState = createConstantState(state, res);
145         if (mLayerState.mNum > 0) {
146             ensurePadding();
147             refreshPadding();
148         }
149     }
150 
createConstantState(@ullable LayerState state, @Nullable Resources res)151     LayerState createConstantState(@Nullable LayerState state, @Nullable Resources res) {
152         return new LayerState(state, this, res);
153     }
154 
155     @Override
inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)156     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
157             throws XmlPullParserException, IOException {
158         super.inflate(r, parser, attrs, theme);
159 
160         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawable);
161         updateStateFromTypedArray(a);
162         a.recycle();
163 
164         inflateLayers(r, parser, attrs, theme);
165 
166         ensurePadding();
167         refreshPadding();
168     }
169 
170     /**
171      * Initializes the constant state from the values in the typed array.
172      */
updateStateFromTypedArray(TypedArray a)173     private void updateStateFromTypedArray(TypedArray a) {
174         final LayerState state = mLayerState;
175 
176         // Account for any configuration changes.
177         state.mChangingConfigurations |= a.getChangingConfigurations();
178 
179         // Extract the theme attributes, if any.
180         state.mThemeAttrs = a.extractThemeAttrs();
181 
182         final int N = a.getIndexCount();
183         for (int i = 0; i < N; i++) {
184             int attr = a.getIndex(i);
185             switch (attr) {
186                 case R.styleable.LayerDrawable_opacity:
187                     state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
188                     break;
189                 case R.styleable.LayerDrawable_paddingTop:
190                     state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
191                     break;
192                 case R.styleable.LayerDrawable_paddingBottom:
193                     state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
194                     break;
195                 case R.styleable.LayerDrawable_paddingLeft:
196                     state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
197                     break;
198                 case R.styleable.LayerDrawable_paddingRight:
199                     state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
200                     break;
201                 case R.styleable.LayerDrawable_paddingStart:
202                     state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
203                     break;
204                 case R.styleable.LayerDrawable_paddingEnd:
205                     state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
206                     break;
207                 case R.styleable.LayerDrawable_autoMirrored:
208                     state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
209                     break;
210                 case R.styleable.LayerDrawable_paddingMode:
211                     state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
212                     break;
213             }
214         }
215     }
216 
217     /**
218      * Inflates child layers using the specified parser.
219      */
inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)220     private void inflateLayers(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
221             throws XmlPullParserException, IOException {
222         final LayerState state = mLayerState;
223 
224         final int innerDepth = parser.getDepth() + 1;
225         int type;
226         int depth;
227         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
228                 && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
229             if (type != XmlPullParser.START_TAG) {
230                 continue;
231             }
232 
233             if (depth > innerDepth || !parser.getName().equals("item")) {
234                 continue;
235             }
236 
237             final ChildDrawable layer = new ChildDrawable();
238             final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.LayerDrawableItem);
239             updateLayerFromTypedArray(layer, a);
240             a.recycle();
241 
242             // If the layer doesn't have a drawable or unresolved theme
243             // attribute for a drawable, attempt to parse one from the child
244             // element.
245             if (layer.mDrawable == null && (layer.mThemeAttrs == null ||
246                     layer.mThemeAttrs[R.styleable.LayerDrawableItem_drawable] == 0)) {
247                 while ((type = parser.next()) == XmlPullParser.TEXT) {
248                 }
249                 if (type != XmlPullParser.START_TAG) {
250                     throw new XmlPullParserException(parser.getPositionDescription()
251                             + ": <item> tag requires a 'drawable' attribute or "
252                             + "child tag defining a drawable");
253                 }
254                 layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
255             }
256 
257             if (layer.mDrawable != null) {
258                 state.mChildrenChangingConfigurations |=
259                         layer.mDrawable.getChangingConfigurations();
260                 layer.mDrawable.setCallback(this);
261             }
262 
263             addLayer(layer);
264         }
265     }
266 
updateLayerFromTypedArray(ChildDrawable layer, TypedArray a)267     private void updateLayerFromTypedArray(ChildDrawable layer, TypedArray a) {
268         final LayerState state = mLayerState;
269 
270         // Account for any configuration changes.
271         state.mChildrenChangingConfigurations |= a.getChangingConfigurations();
272 
273         // Extract the theme attributes, if any.
274         layer.mThemeAttrs = a.extractThemeAttrs();
275 
276         layer.mInsetL = a.getDimensionPixelOffset(
277                 R.styleable.LayerDrawableItem_left, layer.mInsetL);
278         layer.mInsetT = a.getDimensionPixelOffset(
279                 R.styleable.LayerDrawableItem_top, layer.mInsetT);
280         layer.mInsetR = a.getDimensionPixelOffset(
281                 R.styleable.LayerDrawableItem_right, layer.mInsetR);
282         layer.mInsetB = a.getDimensionPixelOffset(
283                 R.styleable.LayerDrawableItem_bottom, layer.mInsetB);
284         layer.mInsetS = a.getDimensionPixelOffset(
285                 R.styleable.LayerDrawableItem_start, layer.mInsetS);
286         layer.mInsetE = a.getDimensionPixelOffset(
287                 R.styleable.LayerDrawableItem_end, layer.mInsetE);
288         layer.mWidth = a.getDimensionPixelSize(
289                 R.styleable.LayerDrawableItem_width, layer.mWidth);
290         layer.mHeight = a.getDimensionPixelSize(
291                 R.styleable.LayerDrawableItem_height, layer.mHeight);
292         layer.mGravity = a.getInteger(
293                 R.styleable.LayerDrawableItem_gravity, layer.mGravity);
294         layer.mId = a.getResourceId(R.styleable.LayerDrawableItem_id, layer.mId);
295 
296         final Drawable dr = a.getDrawable(R.styleable.LayerDrawableItem_drawable);
297         if (dr != null) {
298             layer.mDrawable = dr;
299         }
300     }
301 
302     @Override
applyTheme(Theme t)303     public void applyTheme(Theme t) {
304         super.applyTheme(t);
305 
306         final LayerState state = mLayerState;
307         if (state == null) {
308             return;
309         }
310 
311         if (state.mThemeAttrs != null) {
312             final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.LayerDrawable);
313             updateStateFromTypedArray(a);
314             a.recycle();
315         }
316 
317         final ChildDrawable[] array = state.mChildren;
318         final int N = state.mNum;
319         for (int i = 0; i < N; i++) {
320             final ChildDrawable layer = array[i];
321             if (layer.mThemeAttrs != null) {
322                 final TypedArray a = t.resolveAttributes(layer.mThemeAttrs,
323                         R.styleable.LayerDrawableItem);
324                 updateLayerFromTypedArray(layer, a);
325                 a.recycle();
326             }
327 
328             final Drawable d = layer.mDrawable;
329             if (d != null && d.canApplyTheme()) {
330                 d.applyTheme(t);
331 
332                 // Update cached mask of child changing configurations.
333                 state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
334             }
335         }
336 
337         ensurePadding();
338     }
339 
340     @Override
canApplyTheme()341     public boolean canApplyTheme() {
342         return (mLayerState != null && mLayerState.canApplyTheme()) || super.canApplyTheme();
343     }
344 
345     /**
346      * @hide
347      */
348     @Override
isProjected()349     public boolean isProjected() {
350         if (super.isProjected()) {
351             return true;
352         }
353 
354         final ChildDrawable[] layers = mLayerState.mChildren;
355         final int N = mLayerState.mNum;
356         for (int i = 0; i < N; i++) {
357             if (layers[i].mDrawable.isProjected()) {
358                 return true;
359             }
360         }
361 
362         return false;
363     }
364 
365     /**
366      * Adds a new layer at the end of list of layers and returns its index.
367      *
368      * @param layer The layer to add.
369      * @return The index of the layer.
370      */
addLayer(ChildDrawable layer)371     int addLayer(ChildDrawable layer) {
372         final LayerState st = mLayerState;
373         final int N = st.mChildren != null ? st.mChildren.length : 0;
374         final int i = st.mNum;
375         if (i >= N) {
376             final ChildDrawable[] nu = new ChildDrawable[N + 10];
377             if (i > 0) {
378                 System.arraycopy(st.mChildren, 0, nu, 0, i);
379             }
380 
381             st.mChildren = nu;
382         }
383 
384         st.mChildren[i] = layer;
385         st.mNum++;
386         st.invalidateCache();
387         return i;
388     }
389 
390     /**
391      * Add a new layer to this drawable. The new layer is identified by an id.
392      *
393      * @param dr The drawable to add as a layer.
394      * @param themeAttrs Theme attributes extracted from the layer.
395      * @param id The id of the new layer.
396      * @param left The left padding of the new layer.
397      * @param top The top padding of the new layer.
398      * @param right The right padding of the new layer.
399      * @param bottom The bottom padding of the new layer.
400      */
addLayer(Drawable dr, int[] themeAttrs, int id, int left, int top, int right, int bottom)401     ChildDrawable addLayer(Drawable dr, int[] themeAttrs, int id,
402             int left, int top, int right, int bottom) {
403         final ChildDrawable childDrawable = createLayer(dr);
404         childDrawable.mId = id;
405         childDrawable.mThemeAttrs = themeAttrs;
406         childDrawable.mDrawable.setAutoMirrored(isAutoMirrored());
407         childDrawable.mInsetL = left;
408         childDrawable.mInsetT = top;
409         childDrawable.mInsetR = right;
410         childDrawable.mInsetB = bottom;
411 
412         addLayer(childDrawable);
413 
414         mLayerState.mChildrenChangingConfigurations |= dr.getChangingConfigurations();
415         dr.setCallback(this);
416 
417         return childDrawable;
418     }
419 
createLayer(Drawable dr)420     private ChildDrawable createLayer(Drawable dr) {
421         final ChildDrawable layer = new ChildDrawable();
422         layer.mDrawable = dr;
423         return layer;
424     }
425 
426     /**
427      * Adds a new layer containing the specified {@code drawable} to the end of
428      * the layer list and returns its index.
429      *
430      * @param dr The drawable to add as a new layer.
431      * @return The index of the new layer.
432      */
addLayer(Drawable dr)433     public int addLayer(Drawable dr) {
434         final ChildDrawable layer = createLayer(dr);
435         final int index = addLayer(layer);
436         ensurePadding();
437         refreshChildPadding(index, layer);
438         return index;
439     }
440 
441     /**
442      * Looks for a layer with the given ID and returns its {@link Drawable}.
443      * <p>
444      * If multiple layers are found for the given ID, returns the
445      * {@link Drawable} for the matching layer at the highest index.
446      *
447      * @param id The layer ID to search for.
448      * @return The {@link Drawable} for the highest-indexed layer that has the
449      *         given ID, or null if not found.
450      */
findDrawableByLayerId(int id)451     public Drawable findDrawableByLayerId(int id) {
452         final ChildDrawable[] layers = mLayerState.mChildren;
453         for (int i = mLayerState.mNum - 1; i >= 0; i--) {
454             if (layers[i].mId == id) {
455                 return layers[i].mDrawable;
456             }
457         }
458 
459         return null;
460     }
461 
462     /**
463      * Sets the ID of a layer.
464      *
465      * @param index The index of the layer to modify, must be in the range
466      *              {@code 0...getNumberOfLayers()-1}.
467      * @param id The id to assign to the layer.
468      *
469      * @see #getId(int)
470      * @attr ref android.R.styleable#LayerDrawableItem_id
471      */
setId(int index, int id)472     public void setId(int index, int id) {
473         mLayerState.mChildren[index].mId = id;
474     }
475 
476     /**
477      * Returns the ID of the specified layer.
478      *
479      * @param index The index of the layer, must be in the range
480      *              {@code 0...getNumberOfLayers()-1}.
481      * @return The id of the layer or {@link android.view.View#NO_ID} if the
482      *         layer has no id.
483      *
484      * @see #setId(int, int)
485      * @attr ref android.R.styleable#LayerDrawableItem_id
486      */
getId(int index)487     public int getId(int index) {
488         if (index >= mLayerState.mNum) {
489             throw new IndexOutOfBoundsException();
490         }
491         return mLayerState.mChildren[index].mId;
492     }
493 
494     /**
495      * Returns the number of layers contained within this layer drawable.
496      *
497      * @return The number of layers.
498      */
getNumberOfLayers()499     public int getNumberOfLayers() {
500         return mLayerState.mNum;
501     }
502 
503     /**
504      * Replaces the {@link Drawable} for the layer with the given id.
505      *
506      * @param id The layer ID to search for.
507      * @param drawable The replacement {@link Drawable}.
508      * @return Whether the {@link Drawable} was replaced (could return false if
509      *         the id was not found).
510      */
setDrawableByLayerId(int id, Drawable drawable)511     public boolean setDrawableByLayerId(int id, Drawable drawable) {
512         final int index = findIndexByLayerId(id);
513         if (index < 0) {
514             return false;
515         }
516 
517         setDrawable(index, drawable);
518         return true;
519     }
520 
521     /**
522      * Returns the layer with the specified {@code id}.
523      * <p>
524      * If multiple layers have the same ID, returns the layer with the lowest
525      * index.
526      *
527      * @param id The ID of the layer to return.
528      * @return The index of the layer with the specified ID.
529      */
findIndexByLayerId(int id)530     public int findIndexByLayerId(int id) {
531         final ChildDrawable[] layers = mLayerState.mChildren;
532         final int N = mLayerState.mNum;
533         for (int i = 0; i < N; i++) {
534             final ChildDrawable childDrawable = layers[i];
535             if (childDrawable.mId == id) {
536                 return i;
537             }
538         }
539 
540         return -1;
541     }
542 
543     /**
544      * Sets the drawable for the layer at the specified index.
545      *
546      * @param index The index of the layer to modify, must be in the range
547      *              {@code 0...getNumberOfLayers()-1}.
548      * @param drawable The drawable to set for the layer.
549      *
550      * @see #getDrawable(int)
551      * @attr ref android.R.styleable#LayerDrawableItem_drawable
552      */
setDrawable(int index, Drawable drawable)553     public void setDrawable(int index, Drawable drawable) {
554         if (index >= mLayerState.mNum) {
555             throw new IndexOutOfBoundsException();
556         }
557 
558         final ChildDrawable[] layers = mLayerState.mChildren;
559         final ChildDrawable childDrawable = layers[index];
560         if (childDrawable.mDrawable != null) {
561             if (drawable != null) {
562                 final Rect bounds = childDrawable.mDrawable.getBounds();
563                 drawable.setBounds(bounds);
564             }
565 
566             childDrawable.mDrawable.setCallback(null);
567         }
568 
569         if (drawable != null) {
570             drawable.setCallback(this);
571         }
572 
573         childDrawable.mDrawable = drawable;
574         mLayerState.invalidateCache();
575 
576         refreshChildPadding(index, childDrawable);
577     }
578 
579     /**
580      * Returns the drawable for the layer at the specified index.
581      *
582      * @param index The index of the layer, must be in the range
583      *              {@code 0...getNumberOfLayers()-1}.
584      * @return The {@link Drawable} at the specified layer index.
585      *
586      * @see #setDrawable(int, Drawable)
587      * @attr ref android.R.styleable#LayerDrawableItem_drawable
588      */
getDrawable(int index)589     public Drawable getDrawable(int index) {
590         if (index >= mLayerState.mNum) {
591             throw new IndexOutOfBoundsException();
592         }
593         return mLayerState.mChildren[index].mDrawable;
594     }
595 
596     /**
597      * Sets an explicit size for the specified layer.
598      * <p>
599      * <strong>Note:</strong> Setting an explicit layer size changes the
600      * default layer gravity behavior. See {@link #setLayerGravity(int, int)}
601      * for more information.
602      *
603      * @param index the index of the layer to adjust
604      * @param w width in pixels, or -1 to use the intrinsic width
605      * @param h height in pixels, or -1 to use the intrinsic height
606      * @see #getLayerWidth(int)
607      * @see #getLayerHeight(int)
608      * @attr ref android.R.styleable#LayerDrawableItem_width
609      * @attr ref android.R.styleable#LayerDrawableItem_height
610      */
setLayerSize(int index, int w, int h)611     public void setLayerSize(int index, int w, int h) {
612         final ChildDrawable childDrawable = mLayerState.mChildren[index];
613         childDrawable.mWidth = w;
614         childDrawable.mHeight = h;
615     }
616 
617     /**
618      * @param index the index of the layer to adjust
619      * @param w width in pixels, or -1 to use the intrinsic width
620      * @attr ref android.R.styleable#LayerDrawableItem_width
621      */
setLayerWidth(int index, int w)622     public void setLayerWidth(int index, int w) {
623         final ChildDrawable childDrawable = mLayerState.mChildren[index];
624         childDrawable.mWidth = w;
625     }
626 
627     /**
628      * @param index the index of the drawable to adjust
629      * @return the explicit width of the layer, or -1 if not specified
630      * @see #setLayerSize(int, int, int)
631      * @attr ref android.R.styleable#LayerDrawableItem_width
632      */
getLayerWidth(int index)633     public int getLayerWidth(int index) {
634         final ChildDrawable childDrawable = mLayerState.mChildren[index];
635         return childDrawable.mWidth;
636     }
637 
638     /**
639      * @param index the index of the layer to adjust
640      * @param h height in pixels, or -1 to use the intrinsic height
641      * @attr ref android.R.styleable#LayerDrawableItem_height
642      */
setLayerHeight(int index, int h)643     public void setLayerHeight(int index, int h) {
644         final ChildDrawable childDrawable = mLayerState.mChildren[index];
645         childDrawable.mHeight = h;
646     }
647 
648     /**
649      * @param index the index of the drawable to adjust
650      * @return the explicit height of the layer, or -1 if not specified
651      * @see #setLayerSize(int, int, int)
652      * @attr ref android.R.styleable#LayerDrawableItem_height
653      */
getLayerHeight(int index)654     public int getLayerHeight(int index) {
655         final ChildDrawable childDrawable = mLayerState.mChildren[index];
656         return childDrawable.mHeight;
657     }
658 
659     /**
660      * Sets the gravity used to position or stretch the specified layer within
661      * its container. Gravity is applied after any layer insets (see
662      * {@link #setLayerInset(int, int, int, int, int)}) or padding (see
663      * {@link #setPaddingMode(int)}).
664      * <p>
665      * If gravity is specified as {@link Gravity#NO_GRAVITY}, the default
666      * behavior depends on whether an explicit width or height has been set
667      * (see {@link #setLayerSize(int, int, int)}), If a dimension is not set,
668      * gravity in that direction defaults to {@link Gravity#FILL_HORIZONTAL} or
669      * {@link Gravity#FILL_VERTICAL}; otherwise, gravity in that direction
670      * defaults to {@link Gravity#LEFT} or {@link Gravity#TOP}.
671      *
672      * @param index the index of the drawable to adjust
673      * @param gravity the gravity to set for the layer
674      *
675      * @see #getLayerGravity(int)
676      * @attr ref android.R.styleable#LayerDrawableItem_gravity
677      */
setLayerGravity(int index, int gravity)678     public void setLayerGravity(int index, int gravity) {
679         final ChildDrawable childDrawable = mLayerState.mChildren[index];
680         childDrawable.mGravity = gravity;
681     }
682 
683     /**
684      * @param index the index of the layer
685      * @return the gravity used to position or stretch the specified layer
686      *         within its container
687      *
688      * @see #setLayerGravity(int, int)
689      * @attr ref android.R.styleable#LayerDrawableItem_gravity
690      */
getLayerGravity(int index)691     public int getLayerGravity(int index) {
692         final ChildDrawable childDrawable = mLayerState.mChildren[index];
693         return childDrawable.mGravity;
694     }
695 
696     /**
697      * Specifies the insets in pixels for the drawable at the specified index.
698      *
699      * @param index the index of the drawable to adjust
700      * @param l number of pixels to add to the left bound
701      * @param t number of pixels to add to the top bound
702      * @param r number of pixels to subtract from the right bound
703      * @param b number of pixels to subtract from the bottom bound
704      *
705      * @attr ref android.R.styleable#LayerDrawableItem_left
706      * @attr ref android.R.styleable#LayerDrawableItem_top
707      * @attr ref android.R.styleable#LayerDrawableItem_right
708      * @attr ref android.R.styleable#LayerDrawableItem_bottom
709      */
setLayerInset(int index, int l, int t, int r, int b)710     public void setLayerInset(int index, int l, int t, int r, int b) {
711         setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
712     }
713 
714     /**
715      * Specifies the relative insets in pixels for the drawable at the
716      * specified index.
717      *
718      * @param index the index of the layer to adjust
719      * @param s number of pixels to inset from the start bound
720      * @param t number of pixels to inset from the top bound
721      * @param e number of pixels to inset from the end bound
722      * @param b number of pixels to inset from the bottom bound
723      *
724      * @attr ref android.R.styleable#LayerDrawableItem_start
725      * @attr ref android.R.styleable#LayerDrawableItem_top
726      * @attr ref android.R.styleable#LayerDrawableItem_end
727      * @attr ref android.R.styleable#LayerDrawableItem_bottom
728      */
setLayerInsetRelative(int index, int s, int t, int e, int b)729     public void setLayerInsetRelative(int index, int s, int t, int e, int b) {
730         setLayerInsetInternal(index, 0, t, 0, b, s, e);
731     }
732 
733     /**
734      * @param index the index of the layer to adjust
735      * @param l number of pixels to inset from the left bound
736      * @attr ref android.R.styleable#LayerDrawableItem_left
737      */
setLayerInsetLeft(int index, int l)738     public void setLayerInsetLeft(int index, int l) {
739         final ChildDrawable childDrawable = mLayerState.mChildren[index];
740         childDrawable.mInsetL = l;
741     }
742 
743     /**
744      * @param index the index of the layer
745      * @return number of pixels to inset from the left bound
746      * @attr ref android.R.styleable#LayerDrawableItem_left
747      */
getLayerInsetLeft(int index)748     public int getLayerInsetLeft(int index) {
749         final ChildDrawable childDrawable = mLayerState.mChildren[index];
750         return childDrawable.mInsetL;
751     }
752 
753     /**
754      * @param index the index of the layer to adjust
755      * @param r number of pixels to inset from the right bound
756      * @attr ref android.R.styleable#LayerDrawableItem_right
757      */
setLayerInsetRight(int index, int r)758     public void setLayerInsetRight(int index, int r) {
759         final ChildDrawable childDrawable = mLayerState.mChildren[index];
760         childDrawable.mInsetR = r;
761     }
762 
763     /**
764      * @param index the index of the layer
765      * @return number of pixels to inset from the right bound
766      * @attr ref android.R.styleable#LayerDrawableItem_right
767      */
getLayerInsetRight(int index)768     public int getLayerInsetRight(int index) {
769         final ChildDrawable childDrawable = mLayerState.mChildren[index];
770         return childDrawable.mInsetR;
771     }
772 
773     /**
774      * @param index the index of the layer to adjust
775      * @param t number of pixels to inset from the top bound
776      * @attr ref android.R.styleable#LayerDrawableItem_top
777      */
setLayerInsetTop(int index, int t)778     public void setLayerInsetTop(int index, int t) {
779         final ChildDrawable childDrawable = mLayerState.mChildren[index];
780         childDrawable.mInsetT = t;
781     }
782 
783     /**
784      * @param index the index of the layer
785      * @return number of pixels to inset from the top bound
786      * @attr ref android.R.styleable#LayerDrawableItem_top
787      */
getLayerInsetTop(int index)788     public int getLayerInsetTop(int index) {
789         final ChildDrawable childDrawable = mLayerState.mChildren[index];
790         return childDrawable.mInsetT;
791     }
792 
793     /**
794      * @param index the index of the layer to adjust
795      * @param b number of pixels to inset from the bottom bound
796      * @attr ref android.R.styleable#LayerDrawableItem_bottom
797      */
setLayerInsetBottom(int index, int b)798     public void setLayerInsetBottom(int index, int b) {
799         final ChildDrawable childDrawable = mLayerState.mChildren[index];
800         childDrawable.mInsetB = b;
801     }
802 
803     /**
804      * @param index the index of the layer
805      * @return number of pixels to inset from the bottom bound
806      * @attr ref android.R.styleable#LayerDrawableItem_bottom
807      */
getLayerInsetBottom(int index)808     public int getLayerInsetBottom(int index) {
809         final ChildDrawable childDrawable = mLayerState.mChildren[index];
810         return childDrawable.mInsetB;
811     }
812 
813     /**
814      * @param index the index of the layer to adjust
815      * @param s number of pixels to inset from the start bound
816      * @attr ref android.R.styleable#LayerDrawableItem_start
817      */
setLayerInsetStart(int index, int s)818     public void setLayerInsetStart(int index, int s) {
819         final ChildDrawable childDrawable = mLayerState.mChildren[index];
820         childDrawable.mInsetS = s;
821     }
822 
823     /**
824      * @param index the index of the layer
825      * @return number of pixels to inset from the start bound
826      * @attr ref android.R.styleable#LayerDrawableItem_start
827      */
getLayerInsetStart(int index)828     public int getLayerInsetStart(int index) {
829         final ChildDrawable childDrawable = mLayerState.mChildren[index];
830         return childDrawable.mInsetS;
831     }
832 
833     /**
834      * @param index the index of the layer to adjust
835      * @param e number of pixels to inset from the end bound
836      * @attr ref android.R.styleable#LayerDrawableItem_end
837      */
setLayerInsetEnd(int index, int e)838     public void setLayerInsetEnd(int index, int e) {
839         final ChildDrawable childDrawable = mLayerState.mChildren[index];
840         childDrawable.mInsetE = e;
841     }
842 
843     /**
844      * @param index the index of the layer
845      * @return number of pixels to inset from the end bound
846      * @attr ref android.R.styleable#LayerDrawableItem_end
847      */
getLayerInsetEnd(int index)848     public int getLayerInsetEnd(int index) {
849         final ChildDrawable childDrawable = mLayerState.mChildren[index];
850         return childDrawable.mInsetE;
851     }
852 
setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e)853     private void setLayerInsetInternal(int index, int l, int t, int r, int b, int s, int e) {
854         final ChildDrawable childDrawable = mLayerState.mChildren[index];
855         childDrawable.mInsetL = l;
856         childDrawable.mInsetT = t;
857         childDrawable.mInsetR = r;
858         childDrawable.mInsetB = b;
859         childDrawable.mInsetS = s;
860         childDrawable.mInsetE = e;
861     }
862 
863     /**
864      * Specifies how layer padding should affect the bounds of subsequent
865      * layers. The default value is {@link #PADDING_MODE_NEST}.
866      *
867      * @param mode padding mode, one of:
868      *            <ul>
869      *            <li>{@link #PADDING_MODE_NEST} to nest each layer inside the
870      *            padding of the previous layer
871      *            <li>{@link #PADDING_MODE_STACK} to stack each layer directly
872      *            atop the previous layer
873      *            </ul>
874      *
875      * @see #getPaddingMode()
876      * @attr ref android.R.styleable#LayerDrawable_paddingMode
877      */
setPaddingMode(int mode)878     public void setPaddingMode(int mode) {
879         if (mLayerState.mPaddingMode != mode) {
880             mLayerState.mPaddingMode = mode;
881         }
882     }
883 
884     /**
885      * @return the current padding mode
886      *
887      * @see #setPaddingMode(int)
888      * @attr ref android.R.styleable#LayerDrawable_paddingMode
889      */
getPaddingMode()890     public int getPaddingMode() {
891       return mLayerState.mPaddingMode;
892     }
893 
894     @Override
invalidateDrawable(Drawable who)895     public void invalidateDrawable(Drawable who) {
896         invalidateSelf();
897     }
898 
899     @Override
scheduleDrawable(Drawable who, Runnable what, long when)900     public void scheduleDrawable(Drawable who, Runnable what, long when) {
901         scheduleSelf(what, when);
902     }
903 
904     @Override
unscheduleDrawable(Drawable who, Runnable what)905     public void unscheduleDrawable(Drawable who, Runnable what) {
906         unscheduleSelf(what);
907     }
908 
909     @Override
draw(Canvas canvas)910     public void draw(Canvas canvas) {
911         final ChildDrawable[] array = mLayerState.mChildren;
912         final int N = mLayerState.mNum;
913         for (int i = 0; i < N; i++) {
914             final Drawable dr = array[i].mDrawable;
915             if (dr != null) {
916                 dr.draw(canvas);
917             }
918         }
919     }
920 
921     @Override
getChangingConfigurations()922     public int getChangingConfigurations() {
923         return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
924     }
925 
926     @Override
getPadding(Rect padding)927     public boolean getPadding(Rect padding) {
928         final LayerState layerState = mLayerState;
929         if (layerState.mPaddingMode == PADDING_MODE_NEST) {
930             computeNestedPadding(padding);
931         } else {
932             computeStackedPadding(padding);
933         }
934 
935         // If padding was explicitly specified (e.g. not -1) then override the
936         // computed padding in that dimension.
937         if (layerState.mPaddingTop >= 0) {
938             padding.top = layerState.mPaddingTop;
939         }
940 
941         if (layerState.mPaddingBottom >= 0) {
942             padding.bottom = layerState.mPaddingBottom;
943         }
944 
945         final int paddingRtlLeft;
946         final int paddingRtlRight;
947         if (getLayoutDirection() == LayoutDirection.RTL) {
948             paddingRtlLeft = layerState.mPaddingEnd;
949             paddingRtlRight = layerState.mPaddingStart;
950         } else {
951             paddingRtlLeft = layerState.mPaddingStart;
952             paddingRtlRight = layerState.mPaddingEnd;
953         }
954 
955         final int paddingLeft =  paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
956         if (paddingLeft >= 0) {
957             padding.left = paddingLeft;
958         }
959 
960         final int paddingRight =  paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
961         if (paddingRight >= 0) {
962             padding.right = paddingRight;
963         }
964 
965         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
966     }
967 
968     /**
969      * Sets the absolute padding.
970      * <p>
971      * If padding in a dimension is specified as {@code -1}, the resolved
972      * padding will use the value computed according to the padding mode (see
973      * {@link #setPaddingMode(int)}).
974      * <p>
975      * Calling this method clears any relative padding values previously set
976      * using {@link #setPaddingRelative(int, int, int, int)}.
977      *
978      * @param left the left padding in pixels, or -1 to use computed padding
979      * @param top the top padding in pixels, or -1 to use computed padding
980      * @param right the right padding in pixels, or -1 to use computed padding
981      * @param bottom the bottom padding in pixels, or -1 to use computed
982      *               padding
983      * @attr ref android.R.styleable#LayerDrawable_paddingLeft
984      * @attr ref android.R.styleable#LayerDrawable_paddingTop
985      * @attr ref android.R.styleable#LayerDrawable_paddingRight
986      * @attr ref android.R.styleable#LayerDrawable_paddingBottom
987      * @see #setPaddingRelative(int, int, int, int)
988      */
setPadding(int left, int top, int right, int bottom)989     public void setPadding(int left, int top, int right, int bottom) {
990         final LayerState layerState = mLayerState;
991         layerState.mPaddingLeft = left;
992         layerState.mPaddingTop = top;
993         layerState.mPaddingRight = right;
994         layerState.mPaddingBottom = bottom;
995 
996         // Clear relative padding values.
997         layerState.mPaddingStart = -1;
998         layerState.mPaddingEnd = -1;
999     }
1000 
1001     /**
1002      * Sets the relative padding.
1003      * <p>
1004      * If padding in a dimension is specified as {@code -1}, the resolved
1005      * padding will use the value computed according to the padding mode (see
1006      * {@link #setPaddingMode(int)}).
1007      * <p>
1008      * Calling this method clears any absolute padding values previously set
1009      * using {@link #setPadding(int, int, int, int)}.
1010      *
1011      * @param start the start padding in pixels, or -1 to use computed padding
1012      * @param top the top padding in pixels, or -1 to use computed padding
1013      * @param end the end padding in pixels, or -1 to use computed padding
1014      * @param bottom the bottom padding in pixels, or -1 to use computed
1015      *               padding
1016      * @attr ref android.R.styleable#LayerDrawable_paddingStart
1017      * @attr ref android.R.styleable#LayerDrawable_paddingTop
1018      * @attr ref android.R.styleable#LayerDrawable_paddingEnd
1019      * @attr ref android.R.styleable#LayerDrawable_paddingBottom
1020      * @see #setPadding(int, int, int, int)
1021      */
setPaddingRelative(int start, int top, int end, int bottom)1022     public void setPaddingRelative(int start, int top, int end, int bottom) {
1023         final LayerState layerState = mLayerState;
1024         layerState.mPaddingStart = start;
1025         layerState.mPaddingTop = top;
1026         layerState.mPaddingEnd = end;
1027         layerState.mPaddingBottom = bottom;
1028 
1029         // Clear absolute padding values.
1030         layerState.mPaddingLeft = -1;
1031         layerState.mPaddingRight = -1;
1032     }
1033 
1034     /**
1035      * Returns the left padding in pixels.
1036      * <p>
1037      * A return value of {@code -1} means there is no explicit padding set for
1038      * this dimension. As a result, the value for this dimension returned by
1039      * {@link #getPadding(Rect)} will be computed from the child layers
1040      * according to the padding mode (see {@link #getPaddingMode()}.
1041      *
1042      * @return the left padding in pixels, or -1 if not explicitly specified
1043      * @see #setPadding(int, int, int, int)
1044      * @see #getPadding(Rect)
1045      */
getLeftPadding()1046     public int getLeftPadding() {
1047         return mLayerState.mPaddingLeft;
1048     }
1049 
1050     /**
1051      * Returns the right padding in pixels.
1052      * <p>
1053      * A return value of {@code -1} means there is no explicit padding set for
1054      * this dimension. As a result, the value for this dimension returned by
1055      * {@link #getPadding(Rect)} will be computed from the child layers
1056      * according to the padding mode (see {@link #getPaddingMode()}.
1057      *
1058      * @return the right padding in pixels, or -1 if not explicitly specified
1059      * @see #setPadding(int, int, int, int)
1060      * @see #getPadding(Rect)
1061      */
getRightPadding()1062     public int getRightPadding() {
1063         return mLayerState.mPaddingRight;
1064     }
1065 
1066     /**
1067      * Returns the start padding in pixels.
1068      * <p>
1069      * A return value of {@code -1} means there is no explicit padding set for
1070      * this dimension. As a result, the value for this dimension returned by
1071      * {@link #getPadding(Rect)} will be computed from the child layers
1072      * according to the padding mode (see {@link #getPaddingMode()}.
1073      *
1074      * @return the start padding in pixels, or -1 if not explicitly specified
1075      * @see #setPaddingRelative(int, int, int, int)
1076      * @see #getPadding(Rect)
1077      */
getStartPadding()1078     public int getStartPadding() {
1079         return mLayerState.mPaddingStart;
1080     }
1081 
1082     /**
1083      * Returns the end padding in pixels.
1084      * <p>
1085      * A return value of {@code -1} means there is no explicit padding set for
1086      * this dimension. As a result, the value for this dimension returned by
1087      * {@link #getPadding(Rect)} will be computed from the child layers
1088      * according to the padding mode (see {@link #getPaddingMode()}.
1089      *
1090      * @return the end padding in pixels, or -1 if not explicitly specified
1091      * @see #setPaddingRelative(int, int, int, int)
1092      * @see #getPadding(Rect)
1093      */
getEndPadding()1094     public int getEndPadding() {
1095         return mLayerState.mPaddingEnd;
1096     }
1097 
1098     /**
1099      * Returns the top padding in pixels.
1100      * <p>
1101      * A return value of {@code -1} means there is no explicit padding set for
1102      * this dimension. As a result, the value for this dimension returned by
1103      * {@link #getPadding(Rect)} will be computed from the child layers
1104      * according to the padding mode (see {@link #getPaddingMode()}.
1105      *
1106      * @return the top padding in pixels, or -1 if not explicitly specified
1107      * @see #setPadding(int, int, int, int)
1108      * @see #setPaddingRelative(int, int, int, int)
1109      * @see #getPadding(Rect)
1110      */
getTopPadding()1111     public int getTopPadding() {
1112         return mLayerState.mPaddingTop;
1113     }
1114 
1115     /**
1116      * Returns the bottom padding in pixels.
1117      * <p>
1118      * A return value of {@code -1} means there is no explicit padding set for
1119      * this dimension. As a result, the value for this dimension returned by
1120      * {@link #getPadding(Rect)} will be computed from the child layers
1121      * according to the padding mode (see {@link #getPaddingMode()}.
1122      *
1123      * @return the bottom padding in pixels, or -1 if not explicitly specified
1124      * @see #setPadding(int, int, int, int)
1125      * @see #setPaddingRelative(int, int, int, int)
1126      * @see #getPadding(Rect)
1127      */
getBottomPadding()1128     public int getBottomPadding() {
1129         return mLayerState.mPaddingBottom;
1130     }
1131 
computeNestedPadding(Rect padding)1132     private void computeNestedPadding(Rect padding) {
1133         padding.left = 0;
1134         padding.top = 0;
1135         padding.right = 0;
1136         padding.bottom = 0;
1137 
1138         // Add all the padding.
1139         final ChildDrawable[] array = mLayerState.mChildren;
1140         final int N = mLayerState.mNum;
1141         for (int i = 0; i < N; i++) {
1142             refreshChildPadding(i, array[i]);
1143 
1144             padding.left += mPaddingL[i];
1145             padding.top += mPaddingT[i];
1146             padding.right += mPaddingR[i];
1147             padding.bottom += mPaddingB[i];
1148         }
1149     }
1150 
computeStackedPadding(Rect padding)1151     private void computeStackedPadding(Rect padding) {
1152         padding.left = 0;
1153         padding.top = 0;
1154         padding.right = 0;
1155         padding.bottom = 0;
1156 
1157         // Take the max padding.
1158         final ChildDrawable[] array = mLayerState.mChildren;
1159         final int N = mLayerState.mNum;
1160         for (int i = 0; i < N; i++) {
1161             refreshChildPadding(i, array[i]);
1162 
1163             padding.left = Math.max(padding.left, mPaddingL[i]);
1164             padding.top = Math.max(padding.top, mPaddingT[i]);
1165             padding.right = Math.max(padding.right, mPaddingR[i]);
1166             padding.bottom = Math.max(padding.bottom, mPaddingB[i]);
1167         }
1168     }
1169 
1170     /**
1171      * Populates <code>outline</code> with the first available (non-empty) layer outline.
1172      *
1173      * @param outline Outline in which to place the first available layer outline
1174      */
1175     @Override
getOutline(@onNull Outline outline)1176     public void getOutline(@NonNull Outline outline) {
1177         final ChildDrawable[] array = mLayerState.mChildren;
1178         final int N = mLayerState.mNum;
1179         for (int i = 0; i < N; i++) {
1180             final Drawable dr = array[i].mDrawable;
1181             if (dr != null) {
1182                 dr.getOutline(outline);
1183                 if (!outline.isEmpty()) {
1184                     return;
1185                 }
1186             }
1187         }
1188     }
1189 
1190     @Override
setHotspot(float x, float y)1191     public void setHotspot(float x, float y) {
1192         final ChildDrawable[] array = mLayerState.mChildren;
1193         final int N = mLayerState.mNum;
1194         for (int i = 0; i < N; i++) {
1195             final Drawable dr = array[i].mDrawable;
1196             if (dr != null) {
1197                 dr.setHotspot(x, y);
1198             }
1199         }
1200     }
1201 
1202     @Override
setHotspotBounds(int left, int top, int right, int bottom)1203     public void setHotspotBounds(int left, int top, int right, int bottom) {
1204         final ChildDrawable[] array = mLayerState.mChildren;
1205         final int N = mLayerState.mNum;
1206         for (int i = 0; i < N; i++) {
1207             final Drawable dr = array[i].mDrawable;
1208             if (dr != null) {
1209                 dr.setHotspotBounds(left, top, right, bottom);
1210             }
1211         }
1212 
1213         if (mHotspotBounds == null) {
1214             mHotspotBounds = new Rect(left, top, right, bottom);
1215         } else {
1216             mHotspotBounds.set(left, top, right, bottom);
1217         }
1218     }
1219 
1220     @Override
getHotspotBounds(Rect outRect)1221     public void getHotspotBounds(Rect outRect) {
1222         if (mHotspotBounds != null) {
1223             outRect.set(mHotspotBounds);
1224         } else {
1225             super.getHotspotBounds(outRect);
1226         }
1227     }
1228 
1229     @Override
setVisible(boolean visible, boolean restart)1230     public boolean setVisible(boolean visible, boolean restart) {
1231         final boolean changed = super.setVisible(visible, restart);
1232         final ChildDrawable[] array = mLayerState.mChildren;
1233         final int N = mLayerState.mNum;
1234         for (int i = 0; i < N; i++) {
1235             final Drawable dr = array[i].mDrawable;
1236             if (dr != null) {
1237                 dr.setVisible(visible, restart);
1238             }
1239         }
1240 
1241         return changed;
1242     }
1243 
1244     @Override
setDither(boolean dither)1245     public void setDither(boolean dither) {
1246         final ChildDrawable[] array = mLayerState.mChildren;
1247         final int N = mLayerState.mNum;
1248         for (int i = 0; i < N; i++) {
1249             final Drawable dr = array[i].mDrawable;
1250             if (dr != null) {
1251                 dr.setDither(dither);
1252             }
1253         }
1254     }
1255 
1256     @Override
setAlpha(int alpha)1257     public void setAlpha(int alpha) {
1258         final ChildDrawable[] array = mLayerState.mChildren;
1259         final int N = mLayerState.mNum;
1260         for (int i = 0; i < N; i++) {
1261             final Drawable dr = array[i].mDrawable;
1262             if (dr != null) {
1263                 dr.setAlpha(alpha);
1264             }
1265         }
1266     }
1267 
1268     @Override
getAlpha()1269     public int getAlpha() {
1270         final Drawable dr = getFirstNonNullDrawable();
1271         if (dr != null) {
1272             return dr.getAlpha();
1273         } else {
1274             return super.getAlpha();
1275         }
1276     }
1277 
1278     @Override
setColorFilter(ColorFilter colorFilter)1279     public void setColorFilter(ColorFilter colorFilter) {
1280         final ChildDrawable[] array = mLayerState.mChildren;
1281         final int N = mLayerState.mNum;
1282         for (int i = 0; i < N; i++) {
1283             final Drawable dr = array[i].mDrawable;
1284             if (dr != null) {
1285                 dr.setColorFilter(colorFilter);
1286             }
1287         }
1288     }
1289 
1290     @Override
setTintList(ColorStateList tint)1291     public void setTintList(ColorStateList tint) {
1292         final ChildDrawable[] array = mLayerState.mChildren;
1293         final int N = mLayerState.mNum;
1294         for (int i = 0; i < N; i++) {
1295             final Drawable dr = array[i].mDrawable;
1296             if (dr != null) {
1297                 dr.setTintList(tint);
1298             }
1299         }
1300     }
1301 
1302     @Override
setTintMode(Mode tintMode)1303     public void setTintMode(Mode tintMode) {
1304         final ChildDrawable[] array = mLayerState.mChildren;
1305         final int N = mLayerState.mNum;
1306         for (int i = 0; i < N; i++) {
1307             final Drawable dr = array[i].mDrawable;
1308             if (dr != null) {
1309                 dr.setTintMode(tintMode);
1310             }
1311         }
1312     }
1313 
getFirstNonNullDrawable()1314     private Drawable getFirstNonNullDrawable() {
1315         final ChildDrawable[] array = mLayerState.mChildren;
1316         final int N = mLayerState.mNum;
1317         for (int i = 0; i < N; i++) {
1318             final Drawable dr = array[i].mDrawable;
1319             if (dr != null) {
1320                 return dr;
1321             }
1322         }
1323         return null;
1324     }
1325 
1326     /**
1327      * Sets the opacity of this drawable directly instead of collecting the
1328      * states from the layers.
1329      *
1330      * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
1331      *            PixelFormat.UNKNOWN} for the default behavior
1332      * @see PixelFormat#UNKNOWN
1333      * @see PixelFormat#TRANSLUCENT
1334      * @see PixelFormat#TRANSPARENT
1335      * @see PixelFormat#OPAQUE
1336      */
setOpacity(int opacity)1337     public void setOpacity(int opacity) {
1338         mLayerState.mOpacityOverride = opacity;
1339     }
1340 
1341     @Override
getOpacity()1342     public int getOpacity() {
1343         if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
1344             return mLayerState.mOpacityOverride;
1345         }
1346         return mLayerState.getOpacity();
1347     }
1348 
1349     @Override
setAutoMirrored(boolean mirrored)1350     public void setAutoMirrored(boolean mirrored) {
1351         mLayerState.mAutoMirrored = mirrored;
1352 
1353         final ChildDrawable[] array = mLayerState.mChildren;
1354         final int N = mLayerState.mNum;
1355         for (int i = 0; i < N; i++) {
1356             final Drawable dr = array[i].mDrawable;
1357             if (dr != null) {
1358                 dr.setAutoMirrored(mirrored);
1359             }
1360         }
1361     }
1362 
1363     @Override
isAutoMirrored()1364     public boolean isAutoMirrored() {
1365         return mLayerState.mAutoMirrored;
1366     }
1367 
1368     @Override
isStateful()1369     public boolean isStateful() {
1370         return mLayerState.isStateful();
1371     }
1372 
1373     @Override
onStateChange(int[] state)1374     protected boolean onStateChange(int[] state) {
1375         boolean changed = false;
1376 
1377         final ChildDrawable[] array = mLayerState.mChildren;
1378         final int N = mLayerState.mNum;
1379         for (int i = 0; i < N; i++) {
1380             final Drawable dr = array[i].mDrawable;
1381             if (dr != null && dr.isStateful() && dr.setState(state)) {
1382                 refreshChildPadding(i, array[i]);
1383                 changed = true;
1384             }
1385         }
1386 
1387         if (changed) {
1388             updateLayerBounds(getBounds());
1389         }
1390 
1391         return changed;
1392     }
1393 
1394     @Override
onLevelChange(int level)1395     protected boolean onLevelChange(int level) {
1396         boolean changed = false;
1397 
1398         final ChildDrawable[] array = mLayerState.mChildren;
1399         final int N = mLayerState.mNum;
1400         for (int i = 0; i < N; i++) {
1401             final Drawable dr = array[i].mDrawable;
1402             if (dr != null && dr.setLevel(level)) {
1403                 refreshChildPadding(i, array[i]);
1404                 changed = true;
1405             }
1406         }
1407 
1408         if (changed) {
1409             updateLayerBounds(getBounds());
1410         }
1411 
1412         return changed;
1413     }
1414 
1415     @Override
onBoundsChange(Rect bounds)1416     protected void onBoundsChange(Rect bounds) {
1417         updateLayerBounds(bounds);
1418     }
1419 
updateLayerBounds(Rect bounds)1420     private void updateLayerBounds(Rect bounds) {
1421         int padL = 0;
1422         int padT = 0;
1423         int padR = 0;
1424         int padB = 0;
1425 
1426         final Rect outRect = mTmpOutRect;
1427         final int layoutDirection = getLayoutDirection();
1428         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1429         final ChildDrawable[] array = mLayerState.mChildren;
1430         final int N = mLayerState.mNum;
1431         for (int i = 0; i < N; i++) {
1432             final ChildDrawable r = array[i];
1433             final Drawable d = r.mDrawable;
1434             if (d == null) {
1435                 continue;
1436             }
1437 
1438             final Rect container = mTmpContainer;
1439             container.set(d.getBounds());
1440 
1441             // Take the resolved layout direction into account. If start / end
1442             // padding are defined, they will be resolved (hence overriding) to
1443             // left / right or right / left depending on the resolved layout
1444             // direction. If start / end padding are not defined, use the
1445             // left / right ones.
1446             final int insetL, insetR;
1447             if (layoutDirection == LayoutDirection.RTL) {
1448                 insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
1449                 insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
1450             } else {
1451                 insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
1452                 insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
1453             }
1454 
1455             // Establish containing region based on aggregate padding and
1456             // requested insets for the current layer.
1457             container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
1458                     bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
1459 
1460             // Apply resolved gravity to drawable based on resolved size.
1461             final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight,
1462                     d.getIntrinsicWidth(), d.getIntrinsicHeight());
1463             final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
1464             final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
1465             Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
1466             d.setBounds(outRect);
1467 
1468             if (nest) {
1469                 padL += mPaddingL[i];
1470                 padR += mPaddingR[i];
1471                 padT += mPaddingT[i];
1472                 padB += mPaddingB[i];
1473             }
1474         }
1475     }
1476 
1477     /**
1478      * Resolves layer gravity given explicit gravity and dimensions.
1479      * <p>
1480      * If the client hasn't specified a gravity but has specified an explicit
1481      * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
1482      * preserve legacy behavior.
1483      *
1484      * @param gravity layer gravity
1485      * @param width width of the layer if set, -1 otherwise
1486      * @param height height of the layer if set, -1 otherwise
1487      * @return the default gravity for the layer
1488      */
1489     private static int resolveGravity(int gravity, int width, int height,
1490             int intrinsicWidth, int intrinsicHeight) {
1491         if (!Gravity.isHorizontal(gravity)) {
1492             if (width < 0) {
1493                 gravity |= Gravity.FILL_HORIZONTAL;
1494             } else {
1495                 gravity |= Gravity.START;
1496             }
1497         }
1498 
1499         if (!Gravity.isVertical(gravity)) {
1500             if (height < 0) {
1501                 gravity |= Gravity.FILL_VERTICAL;
1502             } else {
1503                 gravity |= Gravity.TOP;
1504             }
1505         }
1506 
1507         // If a dimension if not specified, either implicitly or explicitly,
1508         // force FILL for that dimension's gravity. This ensures that colors
1509         // are handled correctly and ensures backward compatibility.
1510         if (width < 0 && intrinsicWidth < 0) {
1511             gravity |= Gravity.FILL_HORIZONTAL;
1512         }
1513 
1514         if (height < 0 && intrinsicHeight < 0) {
1515             gravity |= Gravity.FILL_VERTICAL;
1516         }
1517 
1518         return gravity;
1519     }
1520 
1521     @Override
1522     public int getIntrinsicWidth() {
1523         int width = -1;
1524         int padL = 0;
1525         int padR = 0;
1526 
1527         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1528         final ChildDrawable[] array = mLayerState.mChildren;
1529         final int N = mLayerState.mNum;
1530         for (int i = 0; i < N; i++) {
1531             final ChildDrawable r = array[i];
1532             if (r.mDrawable == null) {
1533                 continue;
1534             }
1535 
1536             // Take the resolved layout direction into account. If start / end
1537             // padding are defined, they will be resolved (hence overriding) to
1538             // left / right or right / left depending on the resolved layout
1539             // direction. If start / end padding are not defined, use the
1540             // left / right ones.
1541             final int insetL, insetR;
1542             final int layoutDirection = getLayoutDirection();
1543             if (layoutDirection == LayoutDirection.RTL) {
1544                 insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
1545                 insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
1546             } else {
1547                 insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
1548                 insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
1549             }
1550 
1551             final int minWidth = r.mWidth < 0 ? r.mDrawable.getIntrinsicWidth() : r.mWidth;
1552             final int w = minWidth + insetL + insetR + padL + padR;
1553             if (w > width) {
1554                 width = w;
1555             }
1556 
1557             if (nest) {
1558                 padL += mPaddingL[i];
1559                 padR += mPaddingR[i];
1560             }
1561         }
1562 
1563         return width;
1564     }
1565 
1566     @Override
1567     public int getIntrinsicHeight() {
1568         int height = -1;
1569         int padT = 0;
1570         int padB = 0;
1571 
1572         final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
1573         final ChildDrawable[] array = mLayerState.mChildren;
1574         final int N = mLayerState.mNum;
1575         for (int i = 0; i < N; i++) {
1576             final ChildDrawable r = array[i];
1577             if (r.mDrawable == null) {
1578                 continue;
1579             }
1580 
1581             final int minHeight = r.mHeight < 0 ? r.mDrawable.getIntrinsicHeight() : r.mHeight;
1582             final int h = minHeight + r.mInsetT + r.mInsetB + padT + padB;
1583             if (h > height) {
1584                 height = h;
1585             }
1586 
1587             if (nest) {
1588                 padT += mPaddingT[i];
1589                 padB += mPaddingB[i];
1590             }
1591         }
1592 
1593         return height;
1594     }
1595 
1596     /**
1597      * Refreshes the cached padding values for the specified child.
1598      *
1599      * @return true if the child's padding has changed
1600      */
1601     private boolean refreshChildPadding(int i, ChildDrawable r) {
1602         if (r.mDrawable != null) {
1603             final Rect rect = mTmpRect;
1604             r.mDrawable.getPadding(rect);
1605             if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
1606                     rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
1607                 mPaddingL[i] = rect.left;
1608                 mPaddingT[i] = rect.top;
1609                 mPaddingR[i] = rect.right;
1610                 mPaddingB[i] = rect.bottom;
1611                 return true;
1612             }
1613         }
1614         return false;
1615     }
1616 
1617     /**
1618      * Ensures the child padding caches are large enough.
1619      */
1620     void ensurePadding() {
1621         final int N = mLayerState.mNum;
1622         if (mPaddingL != null && mPaddingL.length >= N) {
1623             return;
1624         }
1625 
1626         mPaddingL = new int[N];
1627         mPaddingT = new int[N];
1628         mPaddingR = new int[N];
1629         mPaddingB = new int[N];
1630     }
1631 
1632     void refreshPadding() {
1633         final int N = mLayerState.mNum;
1634         final ChildDrawable[] array = mLayerState.mChildren;
1635         for (int i = 0; i < N; i++) {
1636             refreshChildPadding(i, array[i]);
1637         }
1638     }
1639 
1640     @Override
1641     public ConstantState getConstantState() {
1642         if (mLayerState.canConstantState()) {
1643             mLayerState.mChangingConfigurations = getChangingConfigurations();
1644             return mLayerState;
1645         }
1646         return null;
1647     }
1648 
1649     @Override
1650     public Drawable mutate() {
1651         if (!mMutated && super.mutate() == this) {
1652             mLayerState = createConstantState(mLayerState, null);
1653             final ChildDrawable[] array = mLayerState.mChildren;
1654             final int N = mLayerState.mNum;
1655             for (int i = 0; i < N; i++) {
1656                 final Drawable dr = array[i].mDrawable;
1657                 if (dr != null) {
1658                     dr.mutate();
1659                 }
1660             }
1661             mMutated = true;
1662         }
1663         return this;
1664     }
1665 
1666     /**
1667      * @hide
1668      */
1669     public void clearMutated() {
1670         super.clearMutated();
1671 
1672         final ChildDrawable[] array = mLayerState.mChildren;
1673         final int N = mLayerState.mNum;
1674         for (int i = 0; i < N; i++) {
1675             final Drawable dr = array[i].mDrawable;
1676             if (dr != null) {
1677                 dr.clearMutated();
1678             }
1679         }
1680         mMutated = false;
1681     }
1682 
1683     @Override
1684     public boolean onLayoutDirectionChanged(@View.ResolvedLayoutDir int layoutDirection) {
1685         boolean changed = false;
1686 
1687         final ChildDrawable[] array = mLayerState.mChildren;
1688         final int N = mLayerState.mNum;
1689         for (int i = 0; i < N; i++) {
1690             final Drawable dr = array[i].mDrawable;
1691             if (dr != null) {
1692                 changed |= dr.setLayoutDirection(layoutDirection);
1693             }
1694         }
1695 
1696         updateLayerBounds(getBounds());
1697         return changed;
1698     }
1699 
1700     static class ChildDrawable {
1701         public Drawable mDrawable;
1702         public int[] mThemeAttrs;
1703         public int mInsetL, mInsetT, mInsetR, mInsetB;
1704         public int mInsetS = UNDEFINED_INSET;
1705         public int mInsetE = UNDEFINED_INSET;
1706         public int mWidth = -1;
1707         public int mHeight = -1;
1708         public int mGravity = Gravity.NO_GRAVITY;
1709         public int mId = View.NO_ID;
1710 
1711         ChildDrawable() {
1712             // Default empty constructor.
1713         }
1714 
1715         ChildDrawable(ChildDrawable orig, LayerDrawable owner, Resources res) {
1716             final Drawable dr = orig.mDrawable;
1717             final Drawable clone;
1718             if (dr != null) {
1719                 final ConstantState cs = dr.getConstantState();
1720                 if (res != null) {
1721                     clone = cs.newDrawable(res);
1722                 } else {
1723                     clone = cs.newDrawable();
1724                 }
1725                 clone.setCallback(owner);
1726                 clone.setLayoutDirection(dr.getLayoutDirection());
1727                 clone.setBounds(dr.getBounds());
1728                 clone.setLevel(dr.getLevel());
1729             } else {
1730                 clone = null;
1731             }
1732 
1733             mDrawable = clone;
1734             mThemeAttrs = orig.mThemeAttrs;
1735             mInsetL = orig.mInsetL;
1736             mInsetT = orig.mInsetT;
1737             mInsetR = orig.mInsetR;
1738             mInsetB = orig.mInsetB;
1739             mInsetS = orig.mInsetS;
1740             mInsetE = orig.mInsetE;
1741             mWidth = orig.mWidth;
1742             mHeight = orig.mHeight;
1743             mGravity = orig.mGravity;
1744             mId = orig.mId;
1745         }
1746 
1747         public boolean canApplyTheme() {
1748             return mThemeAttrs != null
1749                     || (mDrawable != null && mDrawable.canApplyTheme());
1750         }
1751     }
1752 
1753     static class LayerState extends ConstantState {
1754         int mNum;
1755         ChildDrawable[] mChildren;
1756         int[] mThemeAttrs;
1757 
1758         int mPaddingTop = -1;
1759         int mPaddingBottom = -1;
1760         int mPaddingLeft = -1;
1761         int mPaddingRight = -1;
1762         int mPaddingStart = -1;
1763         int mPaddingEnd = -1;
1764         int mOpacityOverride = PixelFormat.UNKNOWN;
1765 
1766         int mChangingConfigurations;
1767         int mChildrenChangingConfigurations;
1768 
1769         private boolean mHaveOpacity;
1770         private int mOpacity;
1771 
1772         private boolean mHaveIsStateful;
1773         private boolean mIsStateful;
1774 
1775         private boolean mAutoMirrored = false;
1776 
1777         private int mPaddingMode = PADDING_MODE_NEST;
1778 
1779         LayerState(LayerState orig, LayerDrawable owner, Resources res) {
1780             if (orig != null) {
1781                 final ChildDrawable[] origChildDrawable = orig.mChildren;
1782                 final int N = orig.mNum;
1783 
1784                 mNum = N;
1785                 mChildren = new ChildDrawable[N];
1786 
1787                 mChangingConfigurations = orig.mChangingConfigurations;
1788                 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
1789 
1790                 for (int i = 0; i < N; i++) {
1791                     final ChildDrawable or = origChildDrawable[i];
1792                     mChildren[i] = new ChildDrawable(or, owner, res);
1793                 }
1794 
1795                 mHaveOpacity = orig.mHaveOpacity;
1796                 mOpacity = orig.mOpacity;
1797                 mHaveIsStateful = orig.mHaveIsStateful;
1798                 mIsStateful = orig.mIsStateful;
1799                 mAutoMirrored = orig.mAutoMirrored;
1800                 mPaddingMode = orig.mPaddingMode;
1801                 mThemeAttrs = orig.mThemeAttrs;
1802                 mPaddingTop = orig.mPaddingTop;
1803                 mPaddingBottom = orig.mPaddingBottom;
1804                 mPaddingLeft = orig.mPaddingLeft;
1805                 mPaddingRight = orig.mPaddingRight;
1806                 mPaddingStart = orig.mPaddingStart;
1807                 mPaddingEnd = orig.mPaddingEnd;
1808                 mOpacityOverride = orig.mOpacityOverride;
1809             } else {
1810                 mNum = 0;
1811                 mChildren = null;
1812             }
1813         }
1814 
1815         @Override
1816         public boolean canApplyTheme() {
1817             if (mThemeAttrs != null || super.canApplyTheme()) {
1818                 return true;
1819             }
1820 
1821             final ChildDrawable[] array = mChildren;
1822             final int N = mNum;
1823             for (int i = 0; i < N; i++) {
1824                 final ChildDrawable layer = array[i];
1825                 if (layer.canApplyTheme()) {
1826                     return true;
1827                 }
1828             }
1829 
1830             return false;
1831         }
1832 
1833         @Override
1834         public Drawable newDrawable() {
1835             return new LayerDrawable(this, null);
1836         }
1837 
1838         @Override
1839         public Drawable newDrawable(Resources res) {
1840             return new LayerDrawable(this, res);
1841         }
1842 
1843         @Override
1844         public int getChangingConfigurations() {
1845             return mChangingConfigurations
1846                     | mChildrenChangingConfigurations;
1847         }
1848 
1849         public final int getOpacity() {
1850             if (mHaveOpacity) {
1851                 return mOpacity;
1852             }
1853 
1854             final ChildDrawable[] array = mChildren;
1855             final int N = mNum;
1856 
1857             // Seek to the first non-null drawable.
1858             int firstIndex = -1;
1859             for (int i = 0; i < N; i++) {
1860                 if (array[i].mDrawable != null) {
1861                     firstIndex = i;
1862                     break;
1863                 }
1864             }
1865 
1866             int op;
1867             if (firstIndex >= 0) {
1868                 op = array[firstIndex].mDrawable.getOpacity();
1869             } else {
1870                 op = PixelFormat.TRANSPARENT;
1871             }
1872 
1873             // Merge all remaining non-null drawables.
1874             for (int i = firstIndex + 1; i < N; i++) {
1875                 final Drawable dr = array[i].mDrawable;
1876                 if (dr != null) {
1877                     op = Drawable.resolveOpacity(op, dr.getOpacity());
1878                 }
1879             }
1880 
1881             mOpacity = op;
1882             mHaveOpacity = true;
1883             return op;
1884         }
1885 
1886         public final boolean isStateful() {
1887             if (mHaveIsStateful) {
1888                 return mIsStateful;
1889             }
1890 
1891             final ChildDrawable[] array = mChildren;
1892             final int N = mNum;
1893             boolean isStateful = false;
1894             for (int i = 0; i < N; i++) {
1895                 final Drawable dr = array[i].mDrawable;
1896                 if (dr != null && dr.isStateful()) {
1897                     isStateful = true;
1898                     break;
1899                 }
1900             }
1901 
1902             mIsStateful = isStateful;
1903             mHaveIsStateful = true;
1904             return isStateful;
1905         }
1906 
1907         public final boolean canConstantState() {
1908             final ChildDrawable[] array = mChildren;
1909             final int N = mNum;
1910             for (int i = 0; i < N; i++) {
1911                 final Drawable dr = array[i].mDrawable;
1912                 if (dr != null && dr.getConstantState() == null) {
1913                     return false;
1914                 }
1915             }
1916 
1917             // Don't cache the result, this method is not called very often.
1918             return true;
1919         }
1920 
1921         public void invalidateCache() {
1922             mHaveOpacity = false;
1923             mHaveIsStateful = false;
1924         }
1925 
1926         @Override
1927         public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
1928             final ChildDrawable[] array = mChildren;
1929             final int N = mNum;
1930             int pixelCount = 0;
1931             for (int i = 0; i < N; i++) {
1932                 final Drawable dr = array[i].mDrawable;
1933                 if (dr != null) {
1934                     final ConstantState state = dr.getConstantState();
1935                     if (state != null) {
1936                         pixelCount += state.addAtlasableBitmaps(atlasList);
1937                     }
1938                 }
1939             }
1940             return pixelCount;
1941         }
1942     }
1943 }
1944 
1945