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