17e82b99953680915596eaf0eb35927388e574ca8Chris Banes/*
27e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Copyright (C) 2015 The Android Open Source Project
37e82b99953680915596eaf0eb35927388e574ca8Chris Banes *
47e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
57e82b99953680915596eaf0eb35927388e574ca8Chris Banes * you may not use this file except in compliance with the License.
67e82b99953680915596eaf0eb35927388e574ca8Chris Banes * You may obtain a copy of the License at
77e82b99953680915596eaf0eb35927388e574ca8Chris Banes *
87e82b99953680915596eaf0eb35927388e574ca8Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
97e82b99953680915596eaf0eb35927388e574ca8Chris Banes *
107e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Unless required by applicable law or agreed to in writing, software
117e82b99953680915596eaf0eb35927388e574ca8Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
127e82b99953680915596eaf0eb35927388e574ca8Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137e82b99953680915596eaf0eb35927388e574ca8Chris Banes * See the License for the specific language governing permissions and
147e82b99953680915596eaf0eb35927388e574ca8Chris Banes * limitations under the License.
157e82b99953680915596eaf0eb35927388e574ca8Chris Banes */
167e82b99953680915596eaf0eb35927388e574ca8Chris Banes
177e82b99953680915596eaf0eb35927388e574ca8Chris Banespackage android.support.v4.graphics.drawable;
187e82b99953680915596eaf0eb35927388e574ca8Chris Banes
197e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.content.res.ColorStateList;
209925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banesimport android.content.res.Resources;
217e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.Canvas;
227e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.ColorFilter;
237e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.PorterDuff;
247e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.Rect;
257e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.Region;
267e82b99953680915596eaf0eb35927388e574ca8Chris Banesimport android.graphics.drawable.Drawable;
279925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banesimport android.support.annotation.NonNull;
289925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banesimport android.support.annotation.Nullable;
297e82b99953680915596eaf0eb35927388e574ca8Chris Banes
307e82b99953680915596eaf0eb35927388e574ca8Chris Banes/**
317e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Drawable which delegates all calls to it's wrapped {@link android.graphics.drawable.Drawable}.
327e82b99953680915596eaf0eb35927388e574ca8Chris Banes * <p>
337e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Also allows backward compatible tinting via a color or {@link ColorStateList}.
347e82b99953680915596eaf0eb35927388e574ca8Chris Banes * This functionality is accessed via static methods in {@code DrawableCompat}.
357e82b99953680915596eaf0eb35927388e574ca8Chris Banes */
36be678137d44778590a6220ae0ef13649e83363a9Chris Banesclass DrawableWrapperDonut extends Drawable
37be678137d44778590a6220ae0ef13649e83363a9Chris Banes        implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
387e82b99953680915596eaf0eb35927388e574ca8Chris Banes
399925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
407e82b99953680915596eaf0eb35927388e574ca8Chris Banes
41c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes    private int mCurrentColor;
42421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes    private PorterDuff.Mode mCurrentMode;
43421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes    private boolean mColorFilterSet;
447e82b99953680915596eaf0eb35927388e574ca8Chris Banes
459925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperState mState;
469925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    private boolean mMutated;
479925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
487e82b99953680915596eaf0eb35927388e574ca8Chris Banes    Drawable mDrawable;
497e82b99953680915596eaf0eb35927388e574ca8Chris Banes
509925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperDonut(@NonNull DrawableWrapperState state, @Nullable Resources res) {
519925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState = state;
529925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateLocalState(res);
539925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
54c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes
559925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
569925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Creates a new wrapper around the specified drawable.
579925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     *
589925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * @param dr the drawable to wrap
599925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
609925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperDonut(@Nullable Drawable dr) {
61ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes        mState = mutateConstantState();
62c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes        // Now set the drawable...
63c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes        setWrappedDrawable(dr);
649925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
659925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
669925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
679925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Initializes local dynamic properties from state. This should be called
689925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * after significant state changes, e.g. from the One True Constructor and
699925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * after inflating or applying a theme.
709925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
719925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    private void updateLocalState(@Nullable Resources res) {
729925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (mState != null && mState.mDrawableState != null) {
739925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            final Drawable dr = newDrawableFromState(mState.mDrawableState, res);
749925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            setWrappedDrawable(dr);
759925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
769925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
779925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
789925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
799925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Allows us to call ConstantState.newDrawable(*) is a API safe way
809925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
819925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    protected Drawable newDrawableFromState(@NonNull Drawable.ConstantState state,
829925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            @Nullable Resources res) {
839925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return state.newDrawable();
847e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
857e82b99953680915596eaf0eb35927388e574ca8Chris Banes
867e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
877e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void draw(Canvas canvas) {
887e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.draw(canvas);
897e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
907e82b99953680915596eaf0eb35927388e574ca8Chris Banes
917e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
927e82b99953680915596eaf0eb35927388e574ca8Chris Banes    protected void onBoundsChange(Rect bounds) {
93b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes        if (mDrawable != null) {
94b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes            mDrawable.setBounds(bounds);
95b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes        }
967e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
977e82b99953680915596eaf0eb35927388e574ca8Chris Banes
987e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
997e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setChangingConfigurations(int configs) {
1007e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setChangingConfigurations(configs);
1017e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1027e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1037e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1047e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getChangingConfigurations() {
1059925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return super.getChangingConfigurations()
1069925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                | (mState != null ? mState.getChangingConfigurations() : 0)
1079925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                | mDrawable.getChangingConfigurations();
1087e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1097e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1107e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1117e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setDither(boolean dither) {
1127e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setDither(dither);
1137e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1147e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1157e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1167e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setFilterBitmap(boolean filter) {
1177e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setFilterBitmap(filter);
1187e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1197e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1207e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1217e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setAlpha(int alpha) {
1227e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setAlpha(alpha);
1237e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1247e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1257e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1267e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setColorFilter(ColorFilter cf) {
1277e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setColorFilter(cf);
1287e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1297e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1307e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1317e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean isStateful() {
132ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes        final ColorStateList tintList = (isCompatTintEnabled() && mState != null)
133ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes                ? mState.mTint
134ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes                : null;
1357797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        return (tintList != null && tintList.isStateful()) || mDrawable.isStateful();
1367e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1377e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1387e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1397e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean setState(final int[] stateSet) {
1407e82b99953680915596eaf0eb35927388e574ca8Chris Banes        boolean handled = mDrawable.setState(stateSet);
1417e82b99953680915596eaf0eb35927388e574ca8Chris Banes        handled = updateTint(stateSet) || handled;
1427e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return handled;
1437e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1447e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1457e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1467e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int[] getState() {
1477e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getState();
1487e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1497e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1507e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1517e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Drawable getCurrent() {
1527e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getCurrent();
1537e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1547e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1557e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1567e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean setVisible(boolean visible, boolean restart) {
1577e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
1587e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1597e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1607e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1617e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getOpacity() {
1627e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getOpacity();
1637e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1647e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1657e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1667e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Region getTransparentRegion() {
1677e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getTransparentRegion();
1687e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1697e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1707e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1717e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getIntrinsicWidth() {
1727e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getIntrinsicWidth();
1737e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1747e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1757e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1767e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getIntrinsicHeight() {
1777e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getIntrinsicHeight();
1787e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1797e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1807e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1817e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getMinimumWidth() {
1827e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getMinimumWidth();
1837e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1847e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1857e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1867e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getMinimumHeight() {
1877e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getMinimumHeight();
1887e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1897e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1907e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1917e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean getPadding(Rect padding) {
1927e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getPadding(padding);
1937e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1947e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1957e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1969925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    @Nullable
1979925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    public ConstantState getConstantState() {
1989925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (mState != null && mState.canConstantState()) {
1999925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mState.mChangingConfigurations = getChangingConfigurations();
2009925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mState;
2019925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
2029925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return null;
2039925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
2049925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2059925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    @Override
2067e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Drawable mutate() {
2079925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (!mMutated && super.mutate() == this) {
2089925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mState = mutateConstantState();
2099925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mDrawable != null) {
2109925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mDrawable.mutate();
2119925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
2129925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mState != null) {
2139925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
2149925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
2159925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mMutated = true;
2167e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
2177e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return this;
2187e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2197e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2207e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2219925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Mutates the constant state and returns the new state.
2229925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * <p>
2239925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * This method should never call the super implementation; it should always
2249925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * mutate and return its own constant state.
2259925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     *
2269925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * @return the new state
2279925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
2284da36b0dfa8436b197314094337fa3102fcca1bcChris Banes    @NonNull
2299925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperState mutateConstantState() {
2309925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return new DrawableWrapperStateDonut(mState, null);
2319925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
2329925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2339925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
2347e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2357e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
2367e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void invalidateDrawable(Drawable who) {
2377e82b99953680915596eaf0eb35927388e574ca8Chris Banes        invalidateSelf();
2387e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2397e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2407e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2417e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2427e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
2437e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void scheduleDrawable(Drawable who, Runnable what, long when) {
2447e82b99953680915596eaf0eb35927388e574ca8Chris Banes        scheduleSelf(what, when);
2457e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2467e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2477e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2487e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2497e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
2507e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void unscheduleDrawable(Drawable who, Runnable what) {
2517e82b99953680915596eaf0eb35927388e574ca8Chris Banes        unscheduleSelf(what);
2527e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2537e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2547e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
2557e82b99953680915596eaf0eb35927388e574ca8Chris Banes    protected boolean onLevelChange(int level) {
2567e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.setLevel(level);
2577e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2587e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2597e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
260be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTint(int tint) {
261be678137d44778590a6220ae0ef13649e83363a9Chris Banes        setTintList(ColorStateList.valueOf(tint));
2627e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2637e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2647e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
265be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTintList(ColorStateList tint) {
2669925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState.mTint = tint;
2679925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateTint(getState());
2687e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2697e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2707e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
271be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTintMode(PorterDuff.Mode tintMode) {
2729925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState.mTintMode = tintMode;
2739925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateTint(getState());
2747e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2757e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2767e82b99953680915596eaf0eb35927388e574ca8Chris Banes    private boolean updateTint(int[] state) {
2777797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        if (!isCompatTintEnabled()) {
2787797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // If compat tinting is not enabled, fail fast
2797797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            return false;
2807797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        }
2817797b9f22c8c404309b778a0966266d2b1a84915Chris Banes
2829925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        final ColorStateList tintList = mState.mTint;
2839925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        final PorterDuff.Mode tintMode = mState.mTintMode;
2849925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2859925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (tintList != null && tintMode != null) {
2869925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
2879925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
2889925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                setColorFilter(color, tintMode);
2897e82b99953680915596eaf0eb35927388e574ca8Chris Banes                mCurrentColor = color;
2909925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mCurrentMode = tintMode;
291421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes                mColorFilterSet = true;
2927e82b99953680915596eaf0eb35927388e574ca8Chris Banes                return true;
2937e82b99953680915596eaf0eb35927388e574ca8Chris Banes            }
294c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes        } else {
295421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes            mColorFilterSet = false;
296c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes            clearColorFilter();
2977e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
2987e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return false;
2997e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3007e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3017e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
3027e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * Returns the wrapped {@link Drawable}
3037e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
304c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes    public final Drawable getWrappedDrawable() {
3057e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable;
3067e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3077e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3087e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
3097e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * Sets the current wrapped {@link Drawable}
3107e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
311c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes    public final void setWrappedDrawable(Drawable dr) {
3127e82b99953680915596eaf0eb35927388e574ca8Chris Banes        if (mDrawable != null) {
3137e82b99953680915596eaf0eb35927388e574ca8Chris Banes            mDrawable.setCallback(null);
3147e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
3157e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3169925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mDrawable = dr;
3179925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3189925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (dr != null) {
3199925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setCallback(this);
3209925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            // Only call setters for data that's stored in the base Drawable.
3219925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setVisible(isVisible(), true);
3229925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setState(getState());
3239925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setLevel(getLevel());
3249925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setBounds(getBounds());
3259925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mState != null) {
3269925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mState.mDrawableState = dr.getConstantState();
3279925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
3287e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
329ac54e3ed316c378f9f339b88811613feb176e75dChris Banes
3307e82b99953680915596eaf0eb35927388e574ca8Chris Banes        invalidateSelf();
3317e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3327797b9f22c8c404309b778a0966266d2b1a84915Chris Banes
3337797b9f22c8c404309b778a0966266d2b1a84915Chris Banes    protected boolean isCompatTintEnabled() {
3347797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        // It's enabled by default on Donut
3357797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        return true;
3367797b9f22c8c404309b778a0966266d2b1a84915Chris Banes    }
3379925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3389925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    protected static abstract class DrawableWrapperState extends Drawable.ConstantState {
3399925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        int mChangingConfigurations;
3409925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        Drawable.ConstantState mDrawableState;
3419925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3429925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        ColorStateList mTint = null;
3439925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
3449925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3459925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
3469925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (orig != null) {
3479925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mChangingConfigurations = orig.mChangingConfigurations;
3489925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mDrawableState = orig.mDrawableState;
3499925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mTint = orig.mTint;
3509925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mTintMode = orig.mTintMode;
3519925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
3529925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3539925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3549925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3559925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public Drawable newDrawable() {
3569925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return newDrawable(null);
3579925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3589925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3599925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public abstract Drawable newDrawable(@Nullable Resources res);
3609925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3619925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3629925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public int getChangingConfigurations() {
3639925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mChangingConfigurations
3649925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                    | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0);
3659925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3669925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3679925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        boolean canConstantState() {
3689925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mDrawableState != null;
3699925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3709925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
3719925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3729925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    private static class DrawableWrapperStateDonut extends DrawableWrapperState {
3739925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        DrawableWrapperStateDonut(
3749925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                @Nullable DrawableWrapperState orig, @Nullable Resources res) {
3759925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            super(orig, res);
3769925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3779925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3789925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3799925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public Drawable newDrawable(@Nullable Resources res) {
3809925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return new DrawableWrapperDonut(this, res);
3819925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3829925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
3837e82b99953680915596eaf0eb35927388e574ca8Chris Banes}
384