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;
298f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viveretteimport android.support.annotation.RequiresApi;
307e82b99953680915596eaf0eb35927388e574ca8Chris Banes
317e82b99953680915596eaf0eb35927388e574ca8Chris Banes/**
3244ece5ceaaee232f9139ccbde40ac4a5e642bdeeChristophe Beyls * Drawable which delegates all calls to its wrapped {@link Drawable}.
338f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette * <p/>
347e82b99953680915596eaf0eb35927388e574ca8Chris Banes * Also allows backward compatible tinting via a color or {@link ColorStateList}.
357e82b99953680915596eaf0eb35927388e574ca8Chris Banes * This functionality is accessed via static methods in {@code DrawableCompat}.
367e82b99953680915596eaf0eb35927388e574ca8Chris Banes */
378f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette
381bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas@RequiresApi(14)
39847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikasclass DrawableWrapperApi14 extends Drawable
40be678137d44778590a6220ae0ef13649e83363a9Chris Banes        implements Drawable.Callback, DrawableWrapper, TintAwareDrawable {
417e82b99953680915596eaf0eb35927388e574ca8Chris Banes
429925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
437e82b99953680915596eaf0eb35927388e574ca8Chris Banes
44c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes    private int mCurrentColor;
45421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes    private PorterDuff.Mode mCurrentMode;
46421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes    private boolean mColorFilterSet;
477e82b99953680915596eaf0eb35927388e574ca8Chris Banes
489925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperState mState;
499925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    private boolean mMutated;
509925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
517e82b99953680915596eaf0eb35927388e574ca8Chris Banes    Drawable mDrawable;
527e82b99953680915596eaf0eb35927388e574ca8Chris Banes
53847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    DrawableWrapperApi14(@NonNull DrawableWrapperState state, @Nullable Resources res) {
549925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState = state;
559925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateLocalState(res);
569925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
57c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes
589925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
599925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Creates a new wrapper around the specified drawable.
609925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     *
619925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * @param dr the drawable to wrap
629925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
63847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    DrawableWrapperApi14(@Nullable Drawable dr) {
64ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes        mState = mutateConstantState();
65c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes        // Now set the drawable...
66c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes        setWrappedDrawable(dr);
679925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
689925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
699925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
709925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Initializes local dynamic properties from state. This should be called
719925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * after significant state changes, e.g. from the One True Constructor and
729925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * after inflating or applying a theme.
739925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
749925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    private void updateLocalState(@Nullable Resources res) {
759925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (mState != null && mState.mDrawableState != null) {
769925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            final Drawable dr = newDrawableFromState(mState.mDrawableState, res);
779925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            setWrappedDrawable(dr);
789925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
799925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
809925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
819925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
829925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Allows us to call ConstantState.newDrawable(*) is a API safe way
839925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
849925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    protected Drawable newDrawableFromState(@NonNull Drawable.ConstantState state,
859925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            @Nullable Resources res) {
86f185f104c4786740765e549d535f9ba1052f96ccKirill Grouchnikov        return state.newDrawable(res);
877e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
887e82b99953680915596eaf0eb35927388e574ca8Chris Banes
897e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
901bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas    public void jumpToCurrentState() {
911bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas        mDrawable.jumpToCurrentState();
921bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas    }
931bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas
941bd3746e05e591865b430fd13dcc41ef10638fb2Aurimas Liutikas    @Override
957e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void draw(Canvas canvas) {
967e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.draw(canvas);
977e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
987e82b99953680915596eaf0eb35927388e574ca8Chris Banes
997e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1007e82b99953680915596eaf0eb35927388e574ca8Chris Banes    protected void onBoundsChange(Rect bounds) {
101b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes        if (mDrawable != null) {
102b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes            mDrawable.setBounds(bounds);
103b1ba192200941f2a8c1eb2796c9fedc781bfe97fChris Banes        }
1047e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1057e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1067e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1077e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setChangingConfigurations(int configs) {
1087e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setChangingConfigurations(configs);
1097e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1107e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1117e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1127e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getChangingConfigurations() {
1139925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return super.getChangingConfigurations()
1149925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                | (mState != null ? mState.getChangingConfigurations() : 0)
1159925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                | mDrawable.getChangingConfigurations();
1167e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1177e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1187e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1197e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setDither(boolean dither) {
1207e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setDither(dither);
1217e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1227e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1237e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1247e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setFilterBitmap(boolean filter) {
1257e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setFilterBitmap(filter);
1267e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1277e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1287e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1297e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setAlpha(int alpha) {
1307e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setAlpha(alpha);
1317e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1327e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1337e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1347e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void setColorFilter(ColorFilter cf) {
1357e82b99953680915596eaf0eb35927388e574ca8Chris Banes        mDrawable.setColorFilter(cf);
1367e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1377e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1387e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1397e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean isStateful() {
140ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes        final ColorStateList tintList = (isCompatTintEnabled() && mState != null)
141ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes                ? mState.mTint
142ffbf42aa3acb41b82cc6c1226c63c3649bedfe49Chris Banes                : null;
1437797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        return (tintList != null && tintList.isStateful()) || mDrawable.isStateful();
1447e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1457e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1467e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1477e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean setState(final int[] stateSet) {
1487e82b99953680915596eaf0eb35927388e574ca8Chris Banes        boolean handled = mDrawable.setState(stateSet);
1497e82b99953680915596eaf0eb35927388e574ca8Chris Banes        handled = updateTint(stateSet) || handled;
1507e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return handled;
1517e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1527e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1537e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1547e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int[] getState() {
1557e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getState();
1567e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1577e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1587e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1597e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Drawable getCurrent() {
1607e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getCurrent();
1617e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1627e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1637e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1647e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean setVisible(boolean visible, boolean restart) {
1657e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return super.setVisible(visible, restart) || mDrawable.setVisible(visible, restart);
1667e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1677e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1687e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1697e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getOpacity() {
1707e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getOpacity();
1717e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1727e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1737e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1747e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Region getTransparentRegion() {
1757e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getTransparentRegion();
1767e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1777e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1787e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1797e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getIntrinsicWidth() {
1807e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getIntrinsicWidth();
1817e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1827e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1837e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1847e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getIntrinsicHeight() {
1857e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getIntrinsicHeight();
1867e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1877e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1887e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1897e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getMinimumWidth() {
1907e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getMinimumWidth();
1917e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1927e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1937e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1947e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public int getMinimumHeight() {
1957e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getMinimumHeight();
1967e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
1977e82b99953680915596eaf0eb35927388e574ca8Chris Banes
1987e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
1997e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public boolean getPadding(Rect padding) {
2007e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.getPadding(padding);
2017e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2027e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2037e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
2049925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    @Nullable
2059925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    public ConstantState getConstantState() {
2069925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (mState != null && mState.canConstantState()) {
2079925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mState.mChangingConfigurations = getChangingConfigurations();
2089925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mState;
2099925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
2109925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        return null;
2119925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
2129925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2139925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    @Override
2147e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public Drawable mutate() {
2159925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (!mMutated && super.mutate() == this) {
2169925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mState = mutateConstantState();
2179925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mDrawable != null) {
2189925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mDrawable.mutate();
2199925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
2209925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mState != null) {
2219925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mState.mDrawableState = mDrawable != null ? mDrawable.getConstantState() : null;
2229925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
2239925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            mMutated = true;
2247e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
2257e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return this;
2267e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2277e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2287e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2299925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * Mutates the constant state and returns the new state.
2309925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * <p>
2319925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * This method should never call the super implementation; it should always
2329925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * mutate and return its own constant state.
2339925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     *
2349925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     * @return the new state
2359925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes     */
2364da36b0dfa8436b197314094337fa3102fcca1bcChris Banes    @NonNull
2379925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    DrawableWrapperState mutateConstantState() {
238f185f104c4786740765e549d535f9ba1052f96ccKirill Grouchnikov        return new DrawableWrapperStateBase(mState, null);
2399925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
2409925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2419925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    /**
2427e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2437e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
244847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    @Override
2457e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void invalidateDrawable(Drawable who) {
2467e82b99953680915596eaf0eb35927388e574ca8Chris Banes        invalidateSelf();
2477e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2487e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2497e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2507e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2517e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
252847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    @Override
2537e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void scheduleDrawable(Drawable who, Runnable what, long when) {
2547e82b99953680915596eaf0eb35927388e574ca8Chris Banes        scheduleSelf(what, when);
2557e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2567e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2577e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
2587e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * {@inheritDoc}
2597e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
260847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    @Override
2617e82b99953680915596eaf0eb35927388e574ca8Chris Banes    public void unscheduleDrawable(Drawable who, Runnable what) {
2627e82b99953680915596eaf0eb35927388e574ca8Chris Banes        unscheduleSelf(what);
2637e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2647e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2657e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
2667e82b99953680915596eaf0eb35927388e574ca8Chris Banes    protected boolean onLevelChange(int level) {
2677e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable.setLevel(level);
2687e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2697e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2707e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
271be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTint(int tint) {
272be678137d44778590a6220ae0ef13649e83363a9Chris Banes        setTintList(ColorStateList.valueOf(tint));
2737e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2747e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2757e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
276be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTintList(ColorStateList tint) {
2779925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState.mTint = tint;
2789925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateTint(getState());
2797e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2807e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2817e82b99953680915596eaf0eb35927388e574ca8Chris Banes    @Override
282be678137d44778590a6220ae0ef13649e83363a9Chris Banes    public void setTintMode(PorterDuff.Mode tintMode) {
2839925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mState.mTintMode = tintMode;
2849925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        updateTint(getState());
2857e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
2867e82b99953680915596eaf0eb35927388e574ca8Chris Banes
2877e82b99953680915596eaf0eb35927388e574ca8Chris Banes    private boolean updateTint(int[] state) {
2887797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        if (!isCompatTintEnabled()) {
2897797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            // If compat tinting is not enabled, fail fast
2907797b9f22c8c404309b778a0966266d2b1a84915Chris Banes            return false;
2917797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        }
2927797b9f22c8c404309b778a0966266d2b1a84915Chris Banes
2939925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        final ColorStateList tintList = mState.mTint;
2949925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        final PorterDuff.Mode tintMode = mState.mTintMode;
2959925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
2969925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (tintList != null && tintMode != null) {
2979925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            final int color = tintList.getColorForState(state, tintList.getDefaultColor());
2989925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (!mColorFilterSet || color != mCurrentColor || tintMode != mCurrentMode) {
2999925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                setColorFilter(color, tintMode);
3007e82b99953680915596eaf0eb35927388e574ca8Chris Banes                mCurrentColor = color;
3019925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mCurrentMode = tintMode;
302421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes                mColorFilterSet = true;
3037e82b99953680915596eaf0eb35927388e574ca8Chris Banes                return true;
3047e82b99953680915596eaf0eb35927388e574ca8Chris Banes            }
305c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes        } else {
306421affab4e0d1c631f759186f8b2975933c9b5a5Chris Banes            mColorFilterSet = false;
307c88bcc13910d9d1a0ddd077e7cb96f3d95bde76cChris Banes            clearColorFilter();
3087e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
3097e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return false;
3107e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3117e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3127e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
3137e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * Returns the wrapped {@link Drawable}
3147e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
315e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
316c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes    public final Drawable getWrappedDrawable() {
3177e82b99953680915596eaf0eb35927388e574ca8Chris Banes        return mDrawable;
3187e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3197e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3207e82b99953680915596eaf0eb35927388e574ca8Chris Banes    /**
3217e82b99953680915596eaf0eb35927388e574ca8Chris Banes     * Sets the current wrapped {@link Drawable}
3227e82b99953680915596eaf0eb35927388e574ca8Chris Banes     */
323e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
324c3b9589f61cbfda8c60344a47f8f87c0ded98554Chris Banes    public final void setWrappedDrawable(Drawable dr) {
3257e82b99953680915596eaf0eb35927388e574ca8Chris Banes        if (mDrawable != null) {
3267e82b99953680915596eaf0eb35927388e574ca8Chris Banes            mDrawable.setCallback(null);
3277e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
3287e82b99953680915596eaf0eb35927388e574ca8Chris Banes
3299925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        mDrawable = dr;
3309925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3319925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        if (dr != null) {
3329925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            dr.setCallback(this);
3339925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            // Only call setters for data that's stored in the base Drawable.
334d8ae10da39a28468fad19ed564799c52354a8d1fChris Banes            setVisible(dr.isVisible(), true);
335d8ae10da39a28468fad19ed564799c52354a8d1fChris Banes            setState(dr.getState());
336d8ae10da39a28468fad19ed564799c52354a8d1fChris Banes            setLevel(dr.getLevel());
337d8ae10da39a28468fad19ed564799c52354a8d1fChris Banes            setBounds(dr.getBounds());
3389925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (mState != null) {
3399925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mState.mDrawableState = dr.getConstantState();
3409925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
3417e82b99953680915596eaf0eb35927388e574ca8Chris Banes        }
342ac54e3ed316c378f9f339b88811613feb176e75dChris Banes
3437e82b99953680915596eaf0eb35927388e574ca8Chris Banes        invalidateSelf();
3447e82b99953680915596eaf0eb35927388e574ca8Chris Banes    }
3457797b9f22c8c404309b778a0966266d2b1a84915Chris Banes
3467797b9f22c8c404309b778a0966266d2b1a84915Chris Banes    protected boolean isCompatTintEnabled() {
34764dbe1d454f1190b3cd8426d09b9119949a10709Kirill Grouchnikov        // It's enabled by default on Gingerbread
3487797b9f22c8c404309b778a0966266d2b1a84915Chris Banes        return true;
3497797b9f22c8c404309b778a0966266d2b1a84915Chris Banes    }
3509925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
351847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas    protected abstract static class DrawableWrapperState extends Drawable.ConstantState {
3529925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        int mChangingConfigurations;
3539925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        Drawable.ConstantState mDrawableState;
3549925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3559925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        ColorStateList mTint = null;
3569925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        PorterDuff.Mode mTintMode = DEFAULT_TINT_MODE;
3579925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3589925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
3599925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            if (orig != null) {
3609925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mChangingConfigurations = orig.mChangingConfigurations;
3619925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mDrawableState = orig.mDrawableState;
3629925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mTint = orig.mTint;
3639925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                mTintMode = orig.mTintMode;
3649925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            }
3659925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3669925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3679925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3689925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public Drawable newDrawable() {
3699925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return newDrawable(null);
3709925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3719925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
372e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas        @Override
3739925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public abstract Drawable newDrawable(@Nullable Resources res);
3749925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3759925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3769925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public int getChangingConfigurations() {
3779925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mChangingConfigurations
3789925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                    | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0);
3799925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3809925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3819925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        boolean canConstantState() {
3829925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            return mDrawableState != null;
3839925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3849925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
3859925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
386f185f104c4786740765e549d535f9ba1052f96ccKirill Grouchnikov    private static class DrawableWrapperStateBase extends DrawableWrapperState {
387f185f104c4786740765e549d535f9ba1052f96ccKirill Grouchnikov        DrawableWrapperStateBase(
3889925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes                @Nullable DrawableWrapperState orig, @Nullable Resources res) {
3899925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes            super(orig, res);
3909925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3919925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes
3929925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        @Override
3939925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        public Drawable newDrawable(@Nullable Resources res) {
394847790c44721520e4ec47f99f8af12d6caf713ffAurimas Liutikas            return new DrawableWrapperApi14(this, res);
3959925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes        }
3969925d4a53e0dbaacf07901fb48907fe1aff9bbbdChris Banes    }
3977e82b99953680915596eaf0eb35927388e574ca8Chris Banes}
398