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

Commit 50954d2b authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Propagate density through AdaptiveIconDrawable and BitmapDrawable

Resources#getDrawableForDensity now propagates the overridden
density through to AdaptiveIconDrawable so that the density can be
propagated to leaf BitmapDrawables correctly.

This enables AdaptiveIconDrawable to support higher resolution
foreground/background bitmaps for use in Launcher.

Bug: 36039665
Test: bit CtsContentTestCases:android.content.res.cts.ResourcesTest
Change-Id: Iaa9a5592626e38e1ff839a76f7c6cfb9e16e5dc1
parent aca24656
Loading
Loading
Loading
Loading
+13 −39
Original line number Diff line number Diff line
@@ -815,14 +815,7 @@ public class Resources {
     */
    public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
            throws NotFoundException {
        final TypedValue value = obtainTempTypedValue();
        try {
            final ResourcesImpl impl = mResourcesImpl;
            impl.getValue(id, value, true);
            return impl.loadDrawable(this, value, id, theme, true);
        } finally {
            releaseTempTypedValue(value);
        }
        return getDrawableForDensity(id, 0, theme);
    }

    /**
@@ -844,7 +837,9 @@ public class Resources {
     *            This integer encodes the package, type, and resource entry.
     *            The value 0 is an invalid identifier.
     * @param density the desired screen density indicated by the resource as
     *            found in {@link DisplayMetrics}.
     *            found in {@link DisplayMetrics}. A value of 0 means to use the
     *            density returned from {@link #getConfiguration()}.
     *            This is equivalent to calling {@link #getDrawable(int)}.
     * @return Drawable An object that can be used to draw this resource.
     * @throws NotFoundException Throws NotFoundException if the given ID does
     *             not exist.
@@ -865,7 +860,9 @@ public class Resources {
     *            This integer encodes the package, type, and resource entry.
     *            The value 0 is an invalid identifier.
     * @param density The desired screen density indicated by the resource as
     *            found in {@link DisplayMetrics}.
     *            found in {@link DisplayMetrics}. A value of 0 means to use the
     *            density returned from {@link #getConfiguration()}.
     *            This is equivalent to calling {@link #getDrawable(int, Theme)}.
     * @param theme The theme used to style the drawable attributes, may be {@code null}.
     * @return Drawable An object that can be used to draw this resource.
     * @throws NotFoundException Throws NotFoundException if the given ID does
@@ -876,37 +873,16 @@ public class Resources {
        try {
            final ResourcesImpl impl = mResourcesImpl;
            impl.getValueForDensity(id, density, value, true);

            // If the drawable's XML lives in our current density qualifier,
            // it's okay to use a scaled version from the cache. Otherwise, we
            // need to actually load the drawable from XML.
            final DisplayMetrics metrics = impl.getDisplayMetrics();
            final boolean useCache = value.density == metrics.densityDpi;

            /*
             * Pretend the requested density is actually the display density. If
             * the drawable returned is not the requested density, then force it
             * to be scaled later by dividing its density by the ratio of
             * requested density to actual device density. Drawables that have
             * undefined density or no density don't need to be handled here.
             */
            if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
                if (value.density == density) {
                    value.density = metrics.densityDpi;
                } else {
                    value.density = (value.density * metrics.densityDpi) / density;
                }
            }
            return impl.loadDrawable(this, value, id, theme, useCache);
            return impl.loadDrawable(this, value, id, density, theme);
        } finally {
            releaseTempTypedValue(value);
        }
    }

    @NonNull
    Drawable loadDrawable(@NonNull TypedValue value, int id, @Nullable Theme theme)
    Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
            throws NotFoundException {
        return mResourcesImpl.loadDrawable(this, value, id, theme, true);
        return mResourcesImpl.loadDrawable(this, value, id, density, theme);
    }

    /**
@@ -1221,8 +1197,7 @@ public class Resources {
     * used to open drawable, sound, and raw resources; it will fail on string
     * and color resources.
     * 
     * @param id The resource identifier to open, as generated by the appt
     *           tool.
     * @param id The resource identifier to open, as generated by the aapt tool.
     * 
     * @return InputStream Access to the resource data.
     *
@@ -1278,7 +1253,7 @@ public class Resources {
     * used to open drawable, sound, and raw resources; it will fail on string
     * and color resources.
     *
     * @param id The resource identifier to open, as generated by the appt tool.
     * @param id The resource identifier to open, as generated by the aapt tool.
     * @param value The TypedValue object to hold the resource information.
     *
     * @return InputStream Access to the resource data.
@@ -1300,8 +1275,7 @@ public class Resources {
     * as uncompressed data, which typically includes things like mp3 files
     * and png images.
     * 
     * @param id The resource identifier to open, as generated by the appt
     *           tool.
     * @param id The resource identifier to open, as generated by the aapt tool.
     * 
     * @return AssetFileDescriptor A new file descriptor you can use to read
     * the resource.  This includes the file descriptor itself, as well as the
+25 −6
Original line number Diff line number Diff line
@@ -523,8 +523,27 @@ public class ResourcesImpl {
    }

    @Nullable
    Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
            boolean useCache) throws NotFoundException {
    Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
            int density, @Nullable Resources.Theme theme)
            throws NotFoundException {
        // If the drawable's XML lives in our current density qualifier,
        // it's okay to use a scaled version from the cache. Otherwise, we
        // need to actually load the drawable from XML.
        final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;

        // Pretend the requested density is actually the display density. If
        // the drawable returned is not the requested density, then force it
        // to be scaled later by dividing its density by the ratio of
        // requested density to actual device density. Drawables that have
        // undefined density or no density don't need to be handled here.
        if (density > 0 && value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
            if (value.density == density) {
                value.density = mMetrics.densityDpi;
            } else {
                value.density = (value.density * mMetrics.densityDpi) / density;
            }
        }

        try {
            if (TRACE_FOR_PRELOAD) {
                // Log only framework resources
@@ -576,7 +595,7 @@ public class ResourcesImpl {
            } else if (isColorDrawable) {
                dr = new ColorDrawable(value.data);
            } else {
                dr = loadDrawableForCookie(wrapper, value, id, null);
                dr = loadDrawableForCookie(wrapper, value, id, density, null);
            }

            // Determine if the drawable has unresolved theme attributes. If it
@@ -691,8 +710,8 @@ public class ResourcesImpl {
    /**
     * Loads a drawable from XML or resources stream.
     */
    private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
            Resources.Theme theme) {
    private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density, @Nullable Resources.Theme theme) {
        if (value.string == null) {
            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
                    + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -722,7 +741,7 @@ public class ResourcesImpl {
            if (file.endsWith(".xml")) {
                final XmlResourceParser rp = loadXmlResourceParser(
                        file, id, value.assetCookie, "drawable");
                dr = Drawable.createFromXml(wrapper, rp, theme);
                dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
                rp.close();
            } else {
                final InputStream is = mAssets.openNonAsset(
+16 −1
Original line number Diff line number Diff line
@@ -923,6 +923,15 @@ public class TypedArray {
     */
    @Nullable
    public Drawable getDrawable(@StyleableRes int index) {
        return getDrawableForDensity(index, 0);
    }

    /**
     * Version of {@link #getDrawable(int)} that accepts an override density.
     * @hide
     */
    @Nullable
    public Drawable getDrawableForDensity(@StyleableRes int index, int density) {
        if (mRecycled) {
            throw new RuntimeException("Cannot make calls to a recycled instance!");
        }
@@ -933,7 +942,13 @@ public class TypedArray {
                throw new UnsupportedOperationException(
                        "Failed to resolve attribute at index " + index + ": " + value);
            }
            return mResources.loadDrawable(value, value.resourceId, mTheme);

            if (density > 0) {
                // If the density is overridden, the value in the TypedArray will not reflect this.
                // Do a separate lookup of the resourceId with the density override.
                mResources.getValueForDensity(value.resourceId, density, value, true);
            }
            return mResources.loadDrawable(value, value.resourceId, density, mTheme);
        }
        return null;
    }
+17 −8
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package android.graphics.drawable;

import static android.graphics.drawable.Drawable.obtainAttributes;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -218,14 +216,16 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback

        // The density may have changed since the last update. This will
        // apply scaling to any existing constant state properties.
        final int density = Drawable.resolveDensity(r, 0);
        state.setDensity(density);
        final int deviceDensity = Drawable.resolveDensity(r, 0);
        state.setDensity(deviceDensity);
        state.mSrcDensityOverride = mSrcDensityOverride;

        final ChildDrawable[] array = state.mChildren;
        for (int i = 0; i < state.mChildren.length; i++) {
            final ChildDrawable layer = array[i];
            layer.setDensity(density);
            layer.setDensity(deviceDensity);
        }

        inflateLayers(r, parser, attrs, theme);
    }

@@ -444,7 +444,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
    /**
     * Inflates child layers using the specified parser.
     */
    void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
    private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final LayerState state = mLayerState;
@@ -491,7 +491,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
                }

                // We found a child drawable. Take ownership.
                layer.mDrawable = Drawable.createFromXmlInner(r, parser, attrs, theme);
                layer.mDrawable = Drawable.createFromXmlInnerForDensity(r, parser, attrs,
                        mLayerState.mSrcDensityOverride, theme);
                layer.mDrawable.setCallback(this);
                state.mChildrenChangingConfigurations |=
                        layer.mDrawable.getChangingConfigurations();
@@ -509,7 +510,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
        // Extract the theme attributes, if any.
        layer.mThemeAttrs = a.extractThemeAttrs();

        Drawable dr = a.getDrawable(R.styleable.AdaptiveIconDrawableLayer_drawable);
        Drawable dr = a.getDrawableForDensity(R.styleable.AdaptiveIconDrawableLayer_drawable,
                state.mSrcDensityOverride);
        if (dr != null) {
            if (layer.mDrawable != null) {
                // It's possible that a drawable was already set, in which case
@@ -951,7 +953,13 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
        final static int N_CHILDREN = 2;
        ChildDrawable[] mChildren;

        // The density at which to render the drawable and its children.
        int mDensity;

        // The density to use when inflating/looking up the children drawables. A value of 0 means
        // use the system's density.
        int mSrcDensityOverride = 0;

        int mOpacityOverride = PixelFormat.UNKNOWN;

        @Config int mChangingConfigurations;
@@ -986,6 +994,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
                mAutoMirrored = orig.mAutoMirrored;
                mThemeAttrs = orig.mThemeAttrs;
                mOpacityOverride = orig.mOpacityOverride;
                mSrcDensityOverride = orig.mSrcDensityOverride;
            } else {
                for (int i = 0; i < N_CHILDREN; i++) {
                    mChildren[i] = new ChildDrawable(mDensity);
+43 −8
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.TypedValue;
import android.view.Gravity;

import com.android.internal.R;
@@ -49,6 +50,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;

/**
 * A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
@@ -749,7 +751,7 @@ public class BitmapDrawable extends Drawable {
        super.inflate(r, parser, attrs, theme);

        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
        updateStateFromTypedArray(a);
        updateStateFromTypedArray(a, mSrcDensityOverride);
        verifyRequiredAttributes(a);
        a.recycle();

@@ -775,7 +777,8 @@ public class BitmapDrawable extends Drawable {
    /**
     * Updates the constant state from the values in the typed array.
     */
    private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
    private void updateStateFromTypedArray(TypedArray a, int srcDensityOverride)
            throws XmlPullParserException {
        final Resources r = a.getResources();
        final BitmapState state = mBitmapState;

@@ -785,9 +788,37 @@ public class BitmapDrawable extends Drawable {
        // Extract the theme attributes, if any.
        state.mThemeAttrs = a.extractThemeAttrs();

        state.mSrcDensityOverride = srcDensityOverride;

        state.mTargetDensity = Drawable.resolveDensity(r, 0);

        final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0);
        if (srcResId != 0) {
            final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
            final TypedValue value = new TypedValue();
            r.getValueForDensity(srcResId, srcDensityOverride, value, true);

            // Pretend the requested density is actually the display density. If
            // the drawable returned is not the requested density, then force it
            // to be scaled later by dividing its density by the ratio of
            // requested density to actual device density. Drawables that have
            // undefined density or no density don't need to be handled here.
            if (srcDensityOverride > 0 && value.density > 0
                    && value.density != TypedValue.DENSITY_NONE) {
                if (value.density == srcDensityOverride) {
                    value.density = r.getDisplayMetrics().densityDpi;
                } else {
                    value.density =
                            (value.density * r.getDisplayMetrics().densityDpi) / srcDensityOverride;
                }
            }

            Bitmap bitmap = null;
            try (InputStream is = r.openRawResource(srcResId, value)) {
                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
            } catch (Exception e) {
                // Do nothing and pick up the error below.
            }

            if (bitmap == null) {
                throw new XmlPullParserException(a.getPositionDescription() +
                        ": <bitmap> requires a valid 'src' attribute");
@@ -796,8 +827,6 @@ public class BitmapDrawable extends Drawable {
            state.mBitmap = bitmap;
        }

        state.mTargetDensity = r.getDisplayMetrics().densityDpi;

        final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false;
        setMipMap(a.getBoolean(R.styleable.BitmapDrawable_mipMap, defMipMap));

@@ -839,8 +868,6 @@ public class BitmapDrawable extends Drawable {
        if (tileModeY != TILE_MODE_UNDEFINED) {
            setTileModeY(parseTileMode(tileModeY));
        }

        state.mTargetDensity = Drawable.resolveDensity(r, 0);
    }

    @Override
@@ -855,7 +882,7 @@ public class BitmapDrawable extends Drawable {
        if (state.mThemeAttrs != null) {
            final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable);
            try {
                updateStateFromTypedArray(a);
                updateStateFromTypedArray(a, state.mSrcDensityOverride);
            } catch (XmlPullParserException e) {
                rethrowAsRuntimeException(e);
            } finally {
@@ -929,7 +956,14 @@ public class BitmapDrawable extends Drawable {
        float mBaseAlpha = 1.0f;
        Shader.TileMode mTileModeX = null;
        Shader.TileMode mTileModeY = null;

        // The density to use when looking up the bitmap in Resources. A value of 0 means use
        // the system's density.
        int mSrcDensityOverride = 0;

        // The density at which to render the bitmap.
        int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;

        boolean mAutoMirrored = false;

        @Config int mChangingConfigurations;
@@ -949,6 +983,7 @@ public class BitmapDrawable extends Drawable {
            mGravity = bitmapState.mGravity;
            mTileModeX = bitmapState.mTileModeX;
            mTileModeY = bitmapState.mTileModeY;
            mSrcDensityOverride = bitmapState.mSrcDensityOverride;
            mTargetDensity = bitmapState.mTargetDensity;
            mBaseAlpha = bitmapState.mBaseAlpha;
            mPaint = new Paint(bitmapState.mPaint);
Loading