Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45c4bbbb authored by Alan Viverette's avatar Alan Viverette
Browse files

Allow use of theme attributes in color state lists

BUG: 17384842
Change-Id: Ibdc413acbd00e37b908432abd55f6521c22b8fc9
parent 4ae0d904
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -2407,6 +2407,7 @@ package android {
    field public static final int Widget_Material_Button_Borderless = 16974425; // 0x1030259
    field public static final int Widget_Material_Button_Borderless_Colored = 16974426; // 0x103025a
    field public static final int Widget_Material_Button_Borderless_Small = 16974427; // 0x103025b
    field public static final int Widget_Material_Button_Colored = 16974547; // 0x10302d3
    field public static final int Widget_Material_Button_Inset = 16974428; // 0x103025c
    field public static final int Widget_Material_Button_Small = 16974429; // 0x103025d
    field public static final int Widget_Material_Button_Toggle = 16974430; // 0x103025e
@@ -7148,6 +7149,8 @@ package android.content {
    method public abstract java.io.File getCacheDir();
    method public abstract java.lang.ClassLoader getClassLoader();
    method public abstract java.io.File getCodeCacheDir();
    method public final int getColor(int);
    method public final android.content.res.ColorStateList getColorStateList(int);
    method public abstract android.content.ContentResolver getContentResolver();
    method public abstract java.io.File getDatabasePath(java.lang.String);
    method public abstract java.io.File getDir(java.lang.String, int);
@@ -9076,7 +9079,10 @@ package android.content.res {
  public class ColorStateList implements android.os.Parcelable {
    ctor public ColorStateList(int[][], int[]);
    method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
    method public void applyTheme(android.content.res.Resources.Theme);
    method public boolean canApplyTheme();
    method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
    method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
    method public int describeContents();
    method public int getColorForState(int[], int);
    method public int getDefaultColor();
@@ -9206,8 +9212,10 @@ package android.content.res {
    method public android.content.res.XmlResourceParser getAnimation(int) throws android.content.res.Resources.NotFoundException;
    method public final android.content.res.AssetManager getAssets();
    method public boolean getBoolean(int) throws android.content.res.Resources.NotFoundException;
    method public int getColor(int) throws android.content.res.Resources.NotFoundException;
    method public android.content.res.ColorStateList getColorStateList(int) throws android.content.res.Resources.NotFoundException;
    method public deprecated int getColor(int) throws android.content.res.Resources.NotFoundException;
    method public int getColor(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
    method public deprecated android.content.res.ColorStateList getColorStateList(int) throws android.content.res.Resources.NotFoundException;
    method public android.content.res.ColorStateList getColorStateList(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException;
    method public android.content.res.Configuration getConfiguration();
    method public float getDimension(int) throws android.content.res.Resources.NotFoundException;
    method public int getDimensionPixelOffset(int) throws android.content.res.Resources.NotFoundException;
@@ -41764,7 +41772,7 @@ package java.lang {
    method public static double nextUp(double);
    method public static float nextUp(float);
    method public static double pow(double, double);
    method public static synchronized double random();
    method public static double random();
    method public static double rint(double);
    method public static long round(double);
    method public static int round(float);
+40 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -392,18 +393,55 @@ public abstract class Context {
    }

    /**
     * Return a drawable object associated with a particular resource ID and
     * Returns a color associated with a particular resource ID and styled for
     * the current theme.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @return A single color value in the form 0xAARRGGBB.
     * @throws android.content.res.Resources.NotFoundException if the given ID
     *         does not exist.
     */
    @Nullable
    public final int getColor(int id) {
        return getResources().getColor(id, getTheme());
    }

    /**
     * Returns a drawable object associated with a particular resource ID and
     * styled for the current theme.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @return Drawable An object that can be used to draw this resource.
     * @return An object that can be used to draw this resource, or
     *         {@code null} if the resource could not be resolved.
     * @throws android.content.res.Resources.NotFoundException if the given ID
     *         does not exist.
     */
    @Nullable
    public final Drawable getDrawable(int id) {
        return getResources().getDrawable(id, getTheme());
    }

    /**
     * Returns a color state list associated with a particular resource ID and
     * styled for the current theme.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @return A color state list, or {@code null} if the resource could not be
     *         resolved.
     * @throws android.content.res.Resources.NotFoundException if the given ID
     *         does not exist.
     */
    @Nullable
    public final ColorStateList getColorStateList(int id) {
        return getResources().getColorStateList(id, getTheme());
    }

     /**
     * Set the base theme for this context.  Note that this should be called
     * before any views are instantiated in the Context (for example before
+302 −98

File changed.

Preview size limit exceeded, changes collapsed.

+172 −89
Original line number Diff line number Diff line
@@ -16,21 +16,20 @@

package android.content.res;

import android.animation.Animator;
import android.animation.StateListAnimator;
import android.annotation.NonNull;
import android.util.Pools.SynchronizedPool;
import android.view.ViewDebug;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.animation.Animator;
import android.animation.StateListAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.res.ColorStateList.ColorStateListFactory;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Bundle;
@@ -40,9 +39,11 @@ import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pools.SynchronizedPool;
import android.util.Slog;
import android.util.TypedValue;
import android.util.LongSparseArray;
import android.view.ViewDebug;

import java.io.IOException;
import java.io.InputStream;
@@ -97,8 +98,8 @@ public class Resources {
    private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
    private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
            = new LongSparseArray<ConstantState>();
    private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists
            = new LongSparseArray<ColorStateList>();
    private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists
            = new LongSparseArray<ColorStateListFactory>();

    // Pool of TypedArrays targeted to this Resources object.
    final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<TypedArray>(5);
@@ -116,8 +117,8 @@ public class Resources {
            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
    private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache =
            new LongSparseArray<WeakReference<ColorStateList>>();
    private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
            new ConfigurationBoundResourceCache<ColorStateList>(this);
    private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
            new ConfigurationBoundResourceCache<Animator>(this);
    private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
@@ -897,20 +898,41 @@ public class Resources {
    }

    /**
     * Return a color integer associated with a particular resource ID.
     * If the resource holds a complex
     * {@link android.content.res.ColorStateList}, then the default color from
     * the set is returned.
     * Returns a color integer associated with a particular resource ID. If the
     * resource holds a complex {@link ColorStateList}, then the default color
     * from the set is returned.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *         not exist.
     *
     * @return Returns a single color value in the form 0xAARRGGBB.
     * @return A single color value in the form 0xAARRGGBB.
     * @deprecated Use {@link #getColor(int, Theme)} instead.
     */
    public int getColor(int id) throws NotFoundException {
        return getColor(id, null);
    }

    /**
     * Returns a themed color integer associated with a particular resource ID.
     * If the resource holds a complex {@link ColorStateList}, then the default
     * color from the set is returned.
     *
     * @param id The desired resource identifier, as generated by the aapt
     *           tool. This integer encodes the package, type, and resource
     *           entry. The value 0 is an invalid identifier.
     * @param theme The theme used to style the color attributes, may be
     *              {@code null}.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *         not exist.
     *
     * @return A single color value in the form 0xAARRGGBB.
     */
    public int getColor(int id, @Nullable Theme theme) throws NotFoundException {
        TypedValue value;
        synchronized (mAccessLock) {
            value = mTmpValue;
@@ -929,30 +951,67 @@ public class Resources {
            }
            mTmpValue = null;
        }
        ColorStateList csl = loadColorStateList(value, id);

        final ColorStateList csl = loadColorStateList(value, id, theme);
        synchronized (mAccessLock) {
            if (mTmpValue == null) {
                mTmpValue = value;
            }
        }

        return csl.getDefaultColor();
    }

    /**
     * Return a color state list associated with a particular resource ID.  The
     * resource may contain either a single raw color value, or a complex
     * {@link android.content.res.ColorStateList} holding multiple possible colors.
     * Returns a color state list associated with a particular resource ID. The
     * resource may contain either a single raw color value or a complex
     * {@link ColorStateList} holding multiple possible colors.
     *
     * @param id The desired resource identifier of a {@link ColorStateList},
     *        as generated by the aapt tool. This integer encodes the package, type, and resource
     *        entry. The value 0 is an invalid identifier.
     *           as generated by the aapt tool. This integer encodes the
     *           package, type, and resource entry. The value 0 is an invalid
     *           identifier.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *         not exist.
     *
     * @return Returns a ColorStateList object containing either a single
     * solid color or multiple colors that can be selected based on a state.
     * @return A ColorStateList object containing either a single solid color
     *         or multiple colors that can be selected based on a state.
     * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
     */
    @Nullable
    public ColorStateList getColorStateList(int id) throws NotFoundException {
        final ColorStateList csl = getColorStateList(id, null);
        if (csl != null && csl.canApplyTheme()) {
            Log.w(TAG, "ColorStateList " + getResourceName(id) + " has "
                    + "unresolved theme attributes! Consider using "
                    + "Resources.getColorStateList(int, Theme) or "
                    + "Context.getColorStateList(int).", new RuntimeException());
        }
        return csl;
    }

    /**
     * Returns a themed color state list associated with a particular resource
     * ID. The resource may contain either a single raw color value or a
     * complex {@link ColorStateList} holding multiple possible colors.
     *
     * @param id The desired resource identifier of a {@link ColorStateList},
     *           as generated by the aapt tool. This integer encodes the
     *           package, type, and resource entry. The value 0 is an invalid
     *           identifier.
     * @param theme The theme used to style the color attributes, may be
     *              {@code null}.
     *
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *         not exist.
     *
     * @return A themed ColorStateList object containing either a single solid
     *         color or multiple colors that can be selected based on a state.
     */
    @Nullable
    public ColorStateList getColorStateList(int id, @Nullable Theme theme)
            throws NotFoundException {
        TypedValue value;
        synchronized (mAccessLock) {
            value = mTmpValue;
@@ -963,15 +1022,19 @@ public class Resources {
            }
            getValue(id, value, true);
        }
        ColorStateList res = loadColorStateList(value, id);

        final ColorStateList res = loadColorStateList(value, id, theme);
        synchronized (mAccessLock) {
            if (mTmpValue == null) {
                mTmpValue = value;
            }
        }

        return res;
    }



    /**
     * Return a boolean associated with a particular resource ID.  This can be
     * used with any integral resource value, and will return true if it is
@@ -1843,11 +1906,10 @@ public class Resources {

            clearDrawableCachesLocked(mDrawableCache, configChanges);
            clearDrawableCachesLocked(mColorDrawableCache, configChanges);
            mColorStateListCache.onConfigurationChange(configChanges);
            mAnimatorCache.onConfigurationChange(configChanges);
            mStateListAnimatorCache.onConfigurationChange(configChanges);

            mColorStateListCache.clear();

            flushLayoutCache();
        }
        synchronized (sSync) {
@@ -2530,7 +2592,8 @@ public class Resources {
        return null;
    }

    /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
    @Nullable
    ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
            throws NotFoundException {
        if (TRACE_FOR_PRELOAD) {
            // Log only framework resources
@@ -2544,101 +2607,107 @@ public class Resources {

        ColorStateList csl;

        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {

            csl = sPreloadedColorStateLists.get(key);
            if (csl != null) {
                return csl;
        // Handle inline color definitions.
        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
            if (factory != null) {
                return factory.newInstance();
            }

            csl = ColorStateList.valueOf(value.data);

            if (mPreloading) {
                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
                        "color")) {
                    sPreloadedColorStateLists.put(key, csl);
                    sPreloadedColorStateLists.put(key, csl.getFactory());
                }
            }

            return csl;
        }

        csl = getCachedColorStateList(key);
        final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;

        csl = cache.get(key, theme);
        if (csl != null) {
            return csl;
        }

        csl = sPreloadedColorStateLists.get(key);
        final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
        if (factory != null) {
            csl = factory.newInstance(this, theme);
        }

        if (csl == null) {
            csl = loadColorStateListForCookie(value, id, theme);
        }

        if (csl != null) {
            if (mPreloading) {
                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
                        "color")) {
                    sPreloadedColorStateLists.put(key, csl.getFactory());
                }
            } else {
                cache.put(key, theme, csl.getFactory());
            }
        }

        return csl;
    }

    private ColorStateList loadColorStateListForCookie(TypedValue value, int id, Theme theme) {
        if (value.string == null) {
            throw new NotFoundException(
                    "Resource is not a ColorStateList (color or path): " + value);
            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
                    + Integer.toHexString(id) + ") is not a ColorStateList: " + value);
        }

        final String file = value.string.toString();

        if (file.endsWith(".xml")) {
        if (TRACE_FOR_MISS_PRELOAD) {
            // Log only framework resources
            if ((id >>> 24) == 0x1) {
                final String name = getResourceName(id);
                if (name != null) {
                    Log.d(TAG, "Loading framework color state list #" + Integer.toHexString(id)
                            + ": " + name + " at " + file);
                }
            }
        }

        if (DEBUG_LOAD) {
            Log.v(TAG, "Loading color state list for cookie " + value.assetCookie + ": " + file);
        }

        final ColorStateList csl;

        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
        if (file.endsWith(".xml")) {
            try {
                final XmlResourceParser rp = loadXmlResourceParser(
                        file, id, value.assetCookie, "colorstatelist");
                csl = ColorStateList.createFromXml(this, rp);
                csl = ColorStateList.createFromXml(this, rp, theme);
                rp.close();
            } catch (Exception e) {
                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
                NotFoundException rnf = new NotFoundException(
                final NotFoundException rnf = new NotFoundException(
                        "File " + file + " from color state list resource ID #0x"
                                + Integer.toHexString(id));
                rnf.initCause(e);
                throw rnf;
            }
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        } else {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            throw new NotFoundException(
                    "File " + file + " from drawable resource ID #0x"
                            + Integer.toHexString(id) + ": .xml extension required");
        }

        if (csl != null) {
            if (mPreloading) {
                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
                        "color")) {
                    sPreloadedColorStateLists.put(key, csl);
                }
            } else {
                synchronized (mAccessLock) {
                    //Log.i(TAG, "Saving cached color state list @ #" +
                    //        Integer.toHexString(key.intValue())
                    //        + " in " + this + ": " + csl);
                    mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
                }
            }
        }
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

        return csl;
    }

    private ColorStateList getCachedColorStateList(long key) {
        synchronized (mAccessLock) {
            WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
            if (wr != null) {   // we have the key
                ColorStateList entry = wr.get();
                if (entry != null) {
                    //Log.i(TAG, "Returning cached color state list @ #" +
                    //        Integer.toHexString(((Integer)key).intValue())
                    //        + " in " + this + ": " + entry);
                    return entry;
                } else {  // our entry has been purged
                    mColorStateListCache.delete(key);
                }
            }
        }
        return null;
    }

    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
            throws NotFoundException {
        synchronized (mAccessLock) {
@@ -2715,6 +2784,20 @@ public class Resources {
        }
    }

    /**
     * Obtains styled attributes from the theme, if available, or unstyled
     * resources if the theme is null.
     *
     * @hide
     */
    public static TypedArray obtainAttributes(
            Resources res, Theme theme, AttributeSet set, int[] attrs) {
        if (theme == null) {
            return res.obtainAttributes(set, attrs);
        }
        return theme.obtainStyledAttributes(set, attrs, 0, 0);
    }

    private Resources() {
        mAssets = AssetManager.getSystem();
        // NOTE: Intentionally leaving this uninitialized (all values set
+25 −8
Original line number Diff line number Diff line
@@ -392,8 +392,8 @@ public class TypedArray {
        } else if (type == TypedValue.TYPE_STRING) {
            final TypedValue value = mValue;
            if (getValueAt(index, value)) {
                ColorStateList csl = mResources.loadColorStateList(
                        value, value.resourceId);
                final ColorStateList csl = mResources.loadColorStateList(
                        value, value.resourceId, mTheme);
                return csl.getDefaultColor();
            }
            return defValue;
@@ -424,7 +424,7 @@ public class TypedArray {
            if (value.type == TypedValue.TYPE_ATTRIBUTE) {
                throw new RuntimeException("Failed to resolve attribute at index " + index);
            }
            return mResources.loadColorStateList(value, value.resourceId);
            return mResources.loadColorStateList(value, value.resourceId, mTheme);
        }
        return null;
    }
@@ -905,12 +905,21 @@ public class TypedArray {
     * Removes the entries from the typed array so that subsequent calls to typed
     * getters will return the default value without crashing.
     *
     * @return an array of length {@link #getIndexCount()} populated with theme
     *         attributes, or null if there are no theme attributes in the typed
     *         array
     * @return An array of length {@link #getIndexCount()} populated with theme
     *         attributes, or {@code null} if there are no theme attributes in
     *         the typed array.
     * @hide
     */
    @Nullable
    public int[] extractThemeAttrs() {
        return extractThemeAttrs(null);
    }

    /**
     * @hide
     */
    @Nullable
    public int[] extractThemeAttrs(@Nullable int[] scrap) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }
@@ -922,6 +931,7 @@ public class TypedArray {
        for (int i = 0; i < N; i++) {
            final int index = i * AssetManager.STYLE_NUM_ENTRIES;
            if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
                // Not an attribute, ignore.
                continue;
            }

@@ -930,13 +940,20 @@ public class TypedArray {

            final int attr = data[index + AssetManager.STYLE_DATA];
            if (attr == 0) {
                // This attribute is useless!
                // Useless data, ignore.
                continue;
            }

            // Ensure we have a usable attribute array.
            if (attrs == null) {
                if (scrap != null && scrap.length == N) {
                    attrs = scrap;
                    Arrays.fill(attrs, 0);
                } else {
                    attrs = new int[N];
                }
            }

            attrs[i] = attr;
        }

Loading