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