19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 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
19ad55abdc748f2cba6955e9e60cf7964dc027f2b5Alan Viveretteimport com.android.internal.R;
20ad55abdc748f2cba6955e9e60cf7964dc027f2b5Alan Viverette
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParser;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParserException;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
25079e23575024e103358c982152afb7a720ae1a8aDianne Hackbornimport java.util.Arrays;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
278dcd533786df8d824f1e040230ee9e7e5b083998Alan Viveretteimport android.annotation.NonNull;
288dcd533786df8d824f1e040230ee9e7e5b083998Alan Viveretteimport android.annotation.Nullable;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
3152b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viveretteimport android.content.res.Resources.Theme;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.StateSet;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * ID value.
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p/>
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>It can be defined in an XML file with the <code>&lt;selector></code> element.
40dfe5c204403bc56c29bb36410574eab8b1950417Scott Main * Each state Drawable is defined in a nested <code>&lt;item></code> element. For more
41dfe5c204403bc56c29bb36410574eab8b1950417Scott Main * information, see the guide to <a
42dfe5c204403bc56c29bb36410574eab8b1950417Scott Main * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#StateListDrawable_visible
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#StateListDrawable_variablePadding
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#StateListDrawable_constantSize
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_focused
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_window_focused
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_enabled
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_checkable
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_checked
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_selected
53079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn * @attr ref android.R.styleable#DrawableStates_state_activated
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_active
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_single
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_first
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_middle
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_last
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @attr ref android.R.styleable#DrawableStates_state_pressed
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class StateListDrawable extends DrawableContainer {
6206ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette    private static final String TAG = "StateListDrawable";
635e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette
64079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn    private static final boolean DEBUG = false;
65079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn
665e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette    private StateListState mStateListState;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mMutated;
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public StateListDrawable() {
70c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn        this(null, null);
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Add a new image/string ID to the set of images.
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param stateSet - An array of resource Ids to associate with the image.
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *                 Switch to this image by calling setState().
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param drawable -The image to show.
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void addState(int[] stateSet, Drawable drawable) {
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (drawable != null) {
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStateListState.addStateSet(stateSet, drawable);
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // in case the new state matches our current state...
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            onStateChange(getState());
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isStateful() {
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected boolean onStateChange(int[] stateSet) {
9506ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette        final boolean changed = super.onStateChange(stateSet);
9606ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int idx = mStateListState.indexOfStateSet(stateSet);
98079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn        if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
99079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn                + Arrays.toString(stateSet) + " found " + idx);
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (idx < 0) {
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10306ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette
10406ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette        return selectDrawable(idx) || changed;
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10852b999f0721b53e9c6e18a4bd664e89aeb65b2d5Alan Viverette    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws XmlPullParserException, IOException {
11039e33621a725bcdaa21a723866e53c6ea3356169Alan Viverette        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.StateListDrawable);
111d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        super.inflateWithAttributes(r, parser, a, R.styleable.StateListDrawable_visible);
112d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        updateStateFromTypedArray(a);
113f1f5f6fcaa768c5b88e9a56f18cbd6ecf72755a8Alan Viverette        updateDensity(r);
114d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        a.recycle();
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
116d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        inflateChildElements(r, parser, attrs, theme);
1176afa16feb32d44c4977b3eb6007246a99f22fdbfJeff Sharkey
118d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        onStateChange(getState());
119d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    }
1203f5a90b2fbba2a83a8a2c5babd5d466a5e0ad2aaFabrice Di Meglio
121d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    /**
122d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette     * Updates the constant state from the values in the typed array.
123d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette     */
124d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    private void updateStateFromTypedArray(TypedArray a) {
125d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        final StateListState state = mStateListState;
126d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette
127d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        // Account for any configuration changes.
128d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mChangingConfigurations |= a.getChangingConfigurations();
129d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette
130d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        // Extract the theme attributes, if any.
131d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mThemeAttrs = a.extractThemeAttrs();
132d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette
133d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mVariablePadding = a.getBoolean(
134d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_variablePadding, state.mVariablePadding);
135d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mConstantSize = a.getBoolean(
136d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_constantSize, state.mConstantSize);
137d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mEnterFadeDuration = a.getInt(
138d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_enterFadeDuration, state.mEnterFadeDuration);
139d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mExitFadeDuration = a.getInt(
140d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_exitFadeDuration, state.mExitFadeDuration);
141d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mDither = a.getBoolean(
142d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_dither, state.mDither);
143d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        state.mAutoMirrored = a.getBoolean(
144d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                R.styleable.StateListDrawable_autoMirrored, state.mAutoMirrored);
145d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    }
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
147d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    /**
148d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette     * Inflates child elements from XML.
149d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette     */
150d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette    private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
151d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette            Theme theme) throws XmlPullParserException, IOException {
152d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        final StateListState state = mStateListState;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int innerDepth = parser.getDepth() + 1;
154ad55abdc748f2cba6955e9e60cf7964dc027f2b5Alan Viverette        int type;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int depth;
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && ((depth = parser.getDepth()) >= innerDepth
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                || type != XmlPullParser.END_TAG)) {
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (type != XmlPullParser.START_TAG) {
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (depth > innerDepth || !parser.getName().equals("item")) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                continue;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
167d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            // This allows state list drawable item elements to be themed at
168d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            // inflation time but does NOT make them work for Zygote preload.
169d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            final TypedArray a = obtainAttributes(r, theme, attrs,
170d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    R.styleable.StateListDrawableItem);
171d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            Drawable dr = a.getDrawable(R.styleable.StateListDrawableItem_drawable);
172d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            a.recycle();
173d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette
174d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            final int[] states = extractStateSet(attrs);
175d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette
176d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            // Loading child elements modifies the state of the AttributeSet's
177d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            // underlying parser, so it needs to happen after obtaining
178d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            // attributes and extracting states.
179d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            if (dr == null) {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while ((type = parser.next()) == XmlPullParser.TEXT) {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (type != XmlPullParser.START_TAG) {
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    throw new XmlPullParserException(
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            parser.getPositionDescription()
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    + ": <item> tag requires a 'drawable' attribute or "
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    + "child tag defining a drawable");
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1886dbe51b50e82057af4d29882889444d22ac19c9cAlan Viverette                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
191d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette            state.addStateSet(states, dr);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
195d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette    /**
196d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette     * Extracts state_ attributes from an attribute set.
197d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette     *
198d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette     * @param attrs The attribute set.
199d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette     * @return An array of state_ attributes.
200d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette     */
201d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette    int[] extractStateSet(AttributeSet attrs) {
202d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        int j = 0;
203d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        final int numAttrs = attrs.getAttributeCount();
204d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        int[] states = new int[numAttrs];
205d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        for (int i = 0; i < numAttrs; i++) {
206d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            final int stateResId = attrs.getAttributeNameResource(i);
207d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            switch (stateResId) {
208d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                case 0:
209d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    break;
210d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                case R.attr.drawable:
211d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                case R.attr.id:
212d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    // Ignore attributes from StateListDrawableItem and
213d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    // AnimatedStateListDrawableItem.
214d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    continue;
215d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                default:
216d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                    states[j++] = attrs.getAttributeBooleanValue(i, false)
217d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette                            ? stateResId : -stateResId;
218d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette            }
219d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        }
220d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        states = StateSet.trimStateSet(states, j);
221d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette        return states;
222d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette    }
223d6570d11e4d1e43c2cfe1d10e27a7786c4283169Alan Viverette
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    StateListState getStateListState() {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mStateListState;
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the number of states contained in this drawable.
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of states contained in this drawable.
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API council
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateSet(int)
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateDrawable(int)
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getStateCount() {
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mStateListState.getChildCount();
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the state set at an index.
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The index of the state set.
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The state set at the index.
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API council
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateCount()
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateDrawable(int)
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int[] getStateSet(int index) {
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mStateListState.mStateSets[index];
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the drawable at an index.
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param index The index of the drawable.
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The drawable at the index.
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API council
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateCount()
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateSet(int)
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Drawable getStateDrawable(int index) {
26372146433322588c1116ee06c27ac758ad09d869cAlan Viverette        return mStateListState.getChild(index);
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
265b3c56086d802ae28888dd97ba1f49bd6cee0b673Alan Viverette
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Gets the index of the drawable with the provided state set.
268b3c56086d802ae28888dd97ba1f49bd6cee0b673Alan Viverette     *
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param stateSet the state set to look up
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the index of the provided state set, or -1 if not found
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API council
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateDrawable(int)
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getStateSet(int)
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getStateDrawableIndex(int[] stateSet) {
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mStateListState.indexOfStateSet(stateSet);
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
278b3c56086d802ae28888dd97ba1f49bd6cee0b673Alan Viverette
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Drawable mutate() {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mMutated && super.mutate() == this) {
2828dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            mStateListState.mutate();
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMutated = true;
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return this;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2888dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette    @Override
2898dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette    StateListState cloneConstantState() {
2908dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette        return new StateListState(mStateListState, this, null);
2918dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette    }
2928dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette
293727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette    /**
294727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette     * @hide
295727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette     */
296727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette    public void clearMutated() {
297727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette        super.clearMutated();
298727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette        mMutated = false;
299727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette    }
300727cae197b123ef764a1f8fbe08a995b000d14c3Alan Viverette
3015e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette    static class StateListState extends DrawableContainerState {
302d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        int[] mThemeAttrs;
303079e23575024e103358c982152afb7a720ae1a8aDianne Hackborn        int[][] mStateSets;
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
305c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn        StateListState(StateListState orig, StateListDrawable owner, Resources res) {
306c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn            super(orig, owner, res);
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (orig != null) {
3098dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette                // Perform a shallow copy and rely on mutate() to deep-copy.
310d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                mThemeAttrs = orig.mThemeAttrs;
3118dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette                mStateSets = orig.mStateSets;
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
313d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette                mThemeAttrs = null;
31472146433322588c1116ee06c27ac758ad09d869cAlan Viverette                mStateSets = new int[getCapacity()][];
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
31806ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette        void mutate() {
3198dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            mThemeAttrs = mThemeAttrs != null ? mThemeAttrs.clone() : null;
3208dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette
3218dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            final int[][] stateSets = new int[mStateSets.length][];
3228dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            for (int i = mStateSets.length - 1; i >= 0; i--) {
3238dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette                stateSets[i] = mStateSets[i] != null ? mStateSets[i].clone() : null;
3248dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            }
32506ff2af68aa1041eeb26778e994e0fe196bf8b1eAlan Viverette            mStateSets = stateSets;
3268dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette        }
3278dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int addStateSet(int[] stateSet, Drawable drawable) {
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int pos = addChild(drawable);
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStateSets[pos] = stateSet;
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return pos;
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3345e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        int indexOfStateSet(int[] stateSet) {
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int[][] stateSets = mStateSets;
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int N = getChildCount();
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < N; i++) {
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return i;
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return -1;
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Drawable newDrawable() {
347c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn            return new StateListDrawable(this, null);
348c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn        }
349c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn
350c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn        @Override
351c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn        public Drawable newDrawable(Resources res) {
352c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn            return new StateListDrawable(this, res);
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
356d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        public boolean canApplyTheme() {
357d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette            return mThemeAttrs != null || super.canApplyTheme();
358d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        }
359d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette
360d21fd9d1ccd2b525f9c004a6cd9ba19a645701abAlan Viverette        @Override
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void growArray(int oldSize, int newSize) {
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super.growArray(oldSize, newSize);
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int[][] newStateSets = new int[newSize][];
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize);
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mStateSets = newStateSets;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
36917cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette    @Override
37017cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette    public void applyTheme(Theme theme) {
37117cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        super.applyTheme(theme);
37217cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette
37317cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette        onStateChange(getState());
37417cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette    }
37517cd4dfe3a05c2eddbcbc76066ff3b13fc3f2c8bAlan Viverette
3768dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette    protected void setConstantState(@NonNull DrawableContainerState state) {
3775e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        super.setConstantState(state);
3785e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette
3798dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette        if (state instanceof StateListState) {
3808dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette            mStateListState = (StateListState) state;
3818dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette        }
3825e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette    }
3835e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette
384c2974809373697147cbe5754835cc871fb93aef1Dianne Hackborn    private StateListDrawable(StateListState state, Resources res) {
3858dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette        // Every state list drawable has its own constant state.
3865e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        final StateListState newState = new StateListState(state, this, res);
3875e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        setConstantState(newState);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        onStateChange(getState());
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3905e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette
3915e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette    /**
3925e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette     * This constructor exists so subclasses can avoid calling the default
3935e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette     * constructor and setting up a StateListDrawable-specific constant state.
3945e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette     */
3958dcd533786df8d824f1e040230ee9e7e5b083998Alan Viverette    StateListDrawable(@Nullable StateListState state) {
3965e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        if (state != null) {
3975e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette            setConstantState(state);
3985e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette        }
3995e92c95d847a68178cf6099c801f82dcb7e4fa47Alan Viverette    }
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
402