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