DrawableWrapper.java revision bd3bfc5285dcacff0a69fecf3baeeeb90d887a58
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.graphics.drawable;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParser;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParserException;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.annotation.NonNull;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.annotation.Nullable;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.ColorFilter;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Insets;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Outline;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PixelFormat;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.PorterDuff;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Collection;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Drawable container with only one child element.
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class DrawableWrapper extends Drawable implements Drawable.Callback {
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private DrawableWrapperState mState;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Drawable mDrawable;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mMutated;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    DrawableWrapper(DrawableWrapperState state, Resources res) {
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mState = state;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        updateLocalState(res);
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Creates a new wrapper around the specified drawable.
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dr the drawable to wrap
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public DrawableWrapper(@Nullable Drawable dr) {
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mState = null;
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDrawable = dr;
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Initializes local dynamic properties from state. This should be called
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * after significant state changes, e.g. from the One True Constructor and
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * after inflating or applying a theme.
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void updateLocalState(Resources res) {
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mState != null && mState.mDrawableState != null) {
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Drawable dr = mState.mDrawableState.newDrawable(res);
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            setDrawable(dr);
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Sets the wrapped drawable.
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param dr the wrapped drawable
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setDrawable(@Nullable Drawable dr) {
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setCallback(null);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDrawable = dr;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (dr != null) {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setCallback(this);
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Only call setters for data that's stored in the base Drawable.
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setVisible(isVisible(), true);
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setState(getState());
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setLevel(getLevel());
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setBounds(getBounds());
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dr.setLayoutDirection(getLayoutDirection());
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mState != null) {
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mState.mDrawableState = dr.getConstantState();
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        invalidateSelf();
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1069461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn
1079461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    /**
1089461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn     * @return the wrapped drawable
1099461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn     */
1109461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    @Nullable
1119461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    public Drawable getDrawable() {
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    void updateStateFromTypedArray(TypedArray a) {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final DrawableWrapperState state = mState;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (state == null) {
1180d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn            return;
1190d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        }
1200d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn
1210d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        // Account for any configuration changes.
1220d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        state.mChangingConfigurations |= a.getChangingConfigurations();
1230d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn
1240d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        // Extract the theme attributes, if any.
1250d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        state.mThemeAttrs = a.extractThemeAttrs();
1260d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn
1270d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        // TODO: Consider using R.styleable.DrawableWrapper_drawable
1280d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn    }
1290d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn
1300d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn    @Override
1310d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn    public void applyTheme(Resources.Theme t) {
1320d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        super.applyTheme(t);
1330d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn
1340d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        final DrawableWrapperState state = mState;
1350d4b9c993d8f1108294f8ccb84eeacd92f051b50Dianne Hackborn        if (state == null) {
136a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn            return;
137a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn        }
138a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn
139a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn        if (mDrawable != null && mDrawable.canApplyTheme()) {
140a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn            mDrawable.applyTheme(t);
141a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn        }
142a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn    }
143a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn
144a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn    @Override
145a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn    public boolean canApplyTheme() {
146a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn        return (mState != null && mState.canApplyTheme()) || super.canApplyTheme();
147a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn    }
148a53de0629f3b94472c0f160f5bbe1090b020feabDianne Hackborn
149a53de0629f3b94472c0f160f5bbe1090b020feabDianne Hackborn    @Override
150a53de0629f3b94472c0f160f5bbe1090b020feabDianne Hackborn    public void invalidateDrawable(Drawable who) {
151a3d487cb13983643d5024e493e4d56c96736f1ebDianne Hackborn        final Callback callback = getCallback();
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (callback != null) {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            callback.invalidateDrawable(this);
154d76ef97930d45e37dba4ac58b376c3067c2eb54aDirk Dougherty        }
155d76ef97930d45e37dba4ac58b376c3067c2eb54aDirk Dougherty    }
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
15886cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn    public void scheduleDrawable(Drawable who, Runnable what, long when) {
15986cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn        final Callback callback = getCallback();
16086cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn        if (callback != null) {
16186cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn            callback.scheduleDrawable(this, what, when);
16286cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn        }
16386cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn    }
16486cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn
16586cbc2bf30a7623b2ad6564cf50ca20f87067ea7Dianne Hackborn    @Override
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void unscheduleDrawable(Drawable who, Runnable what) {
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Callback callback = getCallback();
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (callback != null) {
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            callback.unscheduleDrawable(this, what);
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void draw(@NonNull Canvas canvas) {
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.draw(canvas);
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getChangingConfigurations() {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.getChangingConfigurations()
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                | (mState != null ? mState.mChangingConfigurations : 0)
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                | mDrawable.getChangingConfigurations();
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean getPadding(@NonNull Rect padding) {
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null && mDrawable.getPadding(padding);
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** @hide */
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Insets getOpticalInsets() {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null ? mDrawable.getOpticalInsets() : Insets.NONE;
1969461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    }
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setHotspot(float x, float y) {
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setHotspot(x, y);
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2049461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2069a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn    public void setHotspotBounds(int left, int top, int right, int bottom) {
2079a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn        if (mDrawable != null) {
2089a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn            mDrawable.setHotspotBounds(left, top, right, bottom);
2099a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn        }
2109a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn    }
2119a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn
2129a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn    @Override
2139a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn    public void getHotspotBounds(@NonNull Rect outRect) {
2149a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn        if (mDrawable != null) {
2159a84983a9fd9959671e102045eaf185b83291269Dianne Hackborn            mDrawable.getHotspotBounds(outRect);
2169461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn        } else {
2179461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn            outRect.set(getBounds());
2189461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn        }
2199461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    }
2209461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn
2219461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    @Override
2229461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    public boolean setVisible(boolean visible, boolean restart) {
223354736e196ff79962b3ddb52619a674044d773e2Dianne Hackborn        final boolean superChanged = super.setVisible(visible, restart);
2249461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn        final boolean changed = mDrawable != null && mDrawable.setVisible(visible, restart);
2259461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn        return superChanged | changed;
2269461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn    }
2279461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn
228354736e196ff79962b3ddb52619a674044d773e2Dianne Hackborn    @Override
229354736e196ff79962b3ddb52619a674044d773e2Dianne Hackborn    public void setAlpha(int alpha) {
2309461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn        if (mDrawable != null) {
2319461b6f91f37fd32207da1bd734d9ea9629eb8e5Dianne Hackborn            mDrawable.setAlpha(alpha);
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getAlpha() {
237334031cd07c3bd09d23fce0ebaf946fc6ecfee26Glenn Kasten        return mDrawable != null ? mDrawable.getAlpha() : 255;
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setColorFilter(@Nullable ColorFilter colorFilter) {
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setColorFilter(colorFilter);
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setTintList(@Nullable ColorStateList tint) {
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setTintList(tint);
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setTintMode(tintMode);
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean onLayoutDirectionChange(@View.ResolvedLayoutDir int layoutDirection) {
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null && mDrawable.setLayoutDirection(layoutDirection);
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getOpacity() {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null ? mDrawable.getOpacity() : PixelFormat.TRANSPARENT;
2696098c5d3555860f272d8ddbd353a42f4c8474e1cJohn Spurlock    }
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isStateful() {
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null && mDrawable.isStateful();
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean onStateChange(int[] state) {
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final boolean changed = mDrawable.setState(state);
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (changed) {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                onBoundsChange(getBounds());
2826098c5d3555860f272d8ddbd353a42f4c8474e1cJohn Spurlock            }
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return changed;
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2856098c5d3555860f272d8ddbd353a42f4c8474e1cJohn Spurlock        return false;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2886098c5d3555860f272d8ddbd353a42f4c8474e1cJohn Spurlock    @Override
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean onLevelChange(int level) {
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDrawable != null && mDrawable.setLevel(level);
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onBoundsChange(@NonNull Rect bounds) {
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDrawable != null) {
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDrawable.setBounds(bounds);
297        }
298    }
299
300    @Override
301    public int getIntrinsicWidth() {
302        return mDrawable != null ? mDrawable.getIntrinsicWidth() : -1;
303    }
304
305    @Override
306    public int getIntrinsicHeight() {
307        return mDrawable != null ? mDrawable.getIntrinsicHeight() : -1;
308    }
309
310    @Override
311    public void getOutline(@NonNull Outline outline) {
312        if (mDrawable != null) {
313            mDrawable.getOutline(outline);
314        } else {
315            super.getOutline(outline);
316        }
317    }
318
319    @Override
320    @Nullable
321    public ConstantState getConstantState() {
322        if (mState != null && mState.canConstantState()) {
323            mState.mChangingConfigurations = getChangingConfigurations();
324            return mState;
325        }
326        return null;
327    }
328
329    @Override
330    @NonNull
331    public Drawable mutate() {
332        if (!mMutated && super.mutate() == this) {
333            mState = mutateConstantState();
334            if (mDrawable != null) {
335                mDrawable.mutate();
336            }
337            if (mState != null) {
338                mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
339            }
340            mMutated = true;
341        }
342        return this;
343    }
344
345    /**
346     * Mutates the constant state and returns the new state. Responsible for
347     * updating any local copy.
348     * <p>
349     * This method should never call the super implementation; it should always
350     * mutate and return its own constant state.
351     *
352     * @return the new state
353     */
354    DrawableWrapperState mutateConstantState() {
355        return mState;
356    }
357
358    /**
359     * @hide Only used by the framework for pre-loading resources.
360     */
361    public void clearMutated() {
362        super.clearMutated();
363        if (mDrawable != null) {
364            mDrawable.clearMutated();
365        }
366        mMutated = false;
367    }
368
369    /**
370     * Called during inflation to inflate the child element.
371     */
372    void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
373            Resources.Theme theme) throws XmlPullParserException, IOException {
374        // Drawable specified on the root element takes precedence.
375        if (getDrawable() != null) {
376            return;
377        }
378
379        // Seek to the first child element.
380        Drawable dr = null;
381        int type;
382        final int outerDepth = parser.getDepth();
383        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
384                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
385            if (type == XmlPullParser.START_TAG) {
386                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
387                break;
388            }
389        }
390
391        if (dr != null) {
392            setDrawable(dr);
393        }
394    }
395
396    abstract static class DrawableWrapperState extends Drawable.ConstantState {
397        int[] mThemeAttrs;
398        int mChangingConfigurations;
399
400        Drawable.ConstantState mDrawableState;
401
402        DrawableWrapperState(DrawableWrapperState orig) {
403            if (orig != null) {
404                mThemeAttrs = orig.mThemeAttrs;
405                mChangingConfigurations = orig.mChangingConfigurations;
406                mDrawableState = orig.mDrawableState;
407            }
408        }
409
410        @Override
411        public boolean canApplyTheme() {
412            return mThemeAttrs != null
413                    || (mDrawableState != null && mDrawableState.canApplyTheme())
414                    || super.canApplyTheme();
415        }
416
417        @Override
418        public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
419            final Drawable.ConstantState state = mDrawableState;
420            if (state != null) {
421                return state.addAtlasableBitmaps(atlasList);
422            }
423            return 0;
424        }
425
426        @Override
427        public Drawable newDrawable() {
428            return newDrawable(null);
429        }
430
431        @Override
432        public abstract Drawable newDrawable(Resources res);
433
434        @Override
435        public int getChangingConfigurations() {
436            return mChangingConfigurations;
437        }
438
439        public boolean canConstantState() {
440            return mDrawableState != null;
441        }
442    }
443}
444