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