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

Commit 080d5eb5 authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Support for changing density of DrawableWrapper subclasses"

parents 263e19bf 7e3ede28
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -5018,6 +5018,12 @@ i
        <attr name="autoMirrored" format="boolean" />
    </declare-styleable>

    <!-- Drawable class used to wrap other drawables. -->
    <declare-styleable name="DrawableWrapper">
        <!-- The wrapped drawable. -->
        <attr name="drawable" />
    </declare-styleable>

    <!-- Drawable used to render several states. Each state is represented by
         a child drawable. -->
    <declare-styleable name="StateListDrawable">
@@ -5385,6 +5391,7 @@ i
        <attr name="color" />
    </declare-styleable>

    <!-- Drawable used to wrap and inset another drawable. -->
    <declare-styleable name="InsetDrawable">
        <attr name="visible" />
        <attr name="drawable" />
+50 −42
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
     * Creates a new animated rotating drawable with no wrapped drawable.
     */
    public AnimatedRotateDrawable() {
        this(new AnimatedRotateState(null), null);
        this(new AnimatedRotateState(null, null), null);
    }

    @Override
@@ -126,17 +126,43 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedRotateDrawable);
        super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible);

        // Inflation will advance the XmlPullParser and AttributeSet.
        super.inflate(r, parser, attrs, theme);

        updateStateFromTypedArray(a);
        inflateChildDrawable(r, parser, attrs, theme);
        verifyRequiredAttributes(a);
        a.recycle();

        updateLocalState();
    }

    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
    @Override
    public void applyTheme(@NonNull Theme t) {
        super.applyTheme(t);

        final AnimatedRotateState state = mState;
        if (state == null) {
            return;
        }

        if (state.mThemeAttrs != null) {
            final TypedArray a = t.resolveAttributes(
                    state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
            try {
                updateStateFromTypedArray(a);
                verifyRequiredAttributes(a);
            } catch (XmlPullParserException e) {
                throw new RuntimeException(e);
            } finally {
                a.recycle();
            }
        }

        updateLocalState();
    }

    private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
        // If we're not waiting on a theme, verify required attributes.
        if (getDrawable() == null && (mState.mThemeAttrs == null
                || mState.mThemeAttrs[R.styleable.AnimatedRotateDrawable_drawable] == 0)) {
@@ -146,11 +172,17 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
        }
    }

    @Override
    void updateStateFromTypedArray(TypedArray a) {
        super.updateStateFromTypedArray(a);

    private void updateStateFromTypedArray(@NonNull TypedArray a) {
        final AnimatedRotateState state = mState;
        if (state == null) {
            return;
        }

        // Account for any configuration changes.
        state.mChangingConfigurations |= a.getChangingConfigurations();

        // Extract the theme attributes, if any.
        state.mThemeAttrs = a.extractThemeAttrs();

        if (a.hasValue(R.styleable.AnimatedRotateDrawable_pivotX)) {
            final TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX);
@@ -168,38 +200,6 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
                R.styleable.AnimatedRotateDrawable_framesCount, state.mFramesCount));
        setFramesDuration(a.getInt(
                R.styleable.AnimatedRotateDrawable_frameDuration, state.mFrameDuration));

        final Drawable dr = a.getDrawable(R.styleable.AnimatedRotateDrawable_drawable);
        if (dr != null) {
            setDrawable(dr);
        }
    }

    @Override
    public void applyTheme(@Nullable Theme t) {
        final AnimatedRotateState state = mState;
        if (state == null) {
            return;
        }

        if (state.mThemeAttrs != null) {
            final TypedArray a = t.resolveAttributes(
                    state.mThemeAttrs, R.styleable.AnimatedRotateDrawable);
            try {
                updateStateFromTypedArray(a);
                verifyRequiredAttributes(a);
            } catch (XmlPullParserException e) {
                throw new RuntimeException(e);
            } finally {
                a.recycle();
            }
        }

        // The drawable may have changed as a result of applying the theme, so
        // apply the theme to the wrapped drawable last.
        super.applyTheme(t);

        updateLocalState();
    }

    public void setFramesCount(int framesCount) {
@@ -211,7 +211,15 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
        mState.mFrameDuration = framesDuration;
    }

    @Override
    DrawableWrapperState mutateConstantState() {
        mState = new AnimatedRotateState(mState, null);
        return mState;
    }

    static final class AnimatedRotateState extends DrawableWrapper.DrawableWrapperState {
        private int[] mThemeAttrs;

        boolean mPivotXRel = false;
        float mPivotX = 0;
        boolean mPivotYRel = false;
@@ -219,8 +227,8 @@ public class AnimatedRotateDrawable extends DrawableWrapper implements Animatabl
        int mFrameDuration = 150;
        int mFramesCount = 12;

        public AnimatedRotateState(AnimatedRotateState orig) {
            super(orig);
        public AnimatedRotateState(AnimatedRotateState orig, Resources res) {
            super(orig, res);

            if (orig != null) {
                mPivotXRel = orig.mPivotXRel;
+48 −36
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import com.android.internal.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
@@ -59,7 +61,7 @@ public class ClipDrawable extends DrawableWrapper {
    private ClipState mState;

    ClipDrawable() {
        this(new ClipState(null), null);
        this(new ClipState(null, null), null);
    }

    /**
@@ -72,7 +74,7 @@ public class ClipDrawable extends DrawableWrapper {
     *                   {@link #VERTICAL}
     */
    public ClipDrawable(Drawable drawable, int gravity, int orientation) {
        this(new ClipState(null), null);
        this(new ClipState(null, null), null);

        mState.mGravity = gravity;
        mState.mOrientation = orientation;
@@ -81,45 +83,23 @@ public class ClipDrawable extends DrawableWrapper {
    }

    @Override
    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);

        // Inflation will advance the XmlPullParser and AttributeSet.
        super.inflate(r, parser, attrs, theme);

        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.ClipDrawable);
        updateStateFromTypedArray(a);
        inflateChildDrawable(r, parser, attrs, theme);
        verifyRequiredAttributes(a);
        a.recycle();
    }

    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
        // If we're not waiting on a theme, verify required attributes.
        if (getDrawable() == null && (mState.mThemeAttrs == null
                || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
            throw new XmlPullParserException(a.getPositionDescription()
                    + ": <clip> tag requires a 'drawable' attribute or "
                    + "child tag defining a drawable");
        }
    }

    @Override
    void updateStateFromTypedArray(TypedArray a) {
        super.updateStateFromTypedArray(a);

        final ClipState state = mState;
        state.mOrientation = a.getInt(
                R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
        state.mGravity = a.getInt(
                R.styleable.ClipDrawable_gravity, state.mGravity);

        final Drawable dr = a.getDrawable(R.styleable.ClipDrawable_drawable);
        if (dr != null) {
            setDrawable(dr);
        }
    }
    public void applyTheme(@NonNull Theme t) {
        super.applyTheme(t);

    @Override
    public void applyTheme(Theme t) {
        final ClipState state = mState;
        if (state == null) {
            return;
@@ -136,10 +116,34 @@ public class ClipDrawable extends DrawableWrapper {
                a.recycle();
            }
        }
    }

        // The drawable may have changed as a result of applying the theme, so
        // apply the theme to the wrapped drawable last.
        super.applyTheme(t);
    private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
        // If we're not waiting on a theme, verify required attributes.
        if (getDrawable() == null && (mState.mThemeAttrs == null
                || mState.mThemeAttrs[R.styleable.ClipDrawable_drawable] == 0)) {
            throw new XmlPullParserException(a.getPositionDescription()
                    + ": <clip> tag requires a 'drawable' attribute or "
                    + "child tag defining a drawable");
        }
    }

    private void updateStateFromTypedArray(@NonNull TypedArray a) {
        final ClipState state = mState;
        if (state == null) {
            return;
        }

        // Account for any configuration changes.
        state.mChangingConfigurations |= a.getChangingConfigurations();

        // Extract the theme attributes, if any.
        state.mThemeAttrs = a.extractThemeAttrs();

        state.mOrientation = a.getInt(
                R.styleable.ClipDrawable_clipOrientation, state.mOrientation);
        state.mGravity = a.getInt(
                R.styleable.ClipDrawable_gravity, state.mGravity);
    }

    @Override
@@ -200,12 +204,20 @@ public class ClipDrawable extends DrawableWrapper {
        }
    }

    @Override
    DrawableWrapperState mutateConstantState() {
        mState = new ClipState(mState, null);
        return mState;
    }

    static final class ClipState extends DrawableWrapper.DrawableWrapperState {
        private int[] mThemeAttrs;

        int mOrientation = HORIZONTAL;
        int mGravity = Gravity.LEFT;

        ClipState(ClipState orig) {
            super(orig);
        ClipState(ClipState orig, Resources res) {
            super(orig, res);

            if (orig != null) {
                mOrientation = orig.mOrientation;
+13 −15
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;

import com.android.internal.R;

/**
 * A Drawable is a general abstraction for "something that can be drawn."  Most
 * often you will deal with Drawable as the type of resource retrieved for
@@ -791,8 +793,10 @@ public abstract class Drawable {

    /**
     * Applies the specified theme to this Drawable and its children.
     *
     * @param t the theme to apply
     */
    public void applyTheme(@SuppressWarnings("unused") Theme t) {
    public void applyTheme(@NonNull @SuppressWarnings("unused") Theme t) {
    }

    public boolean canApplyTheme() {
@@ -1177,8 +1181,8 @@ public abstract class Drawable {
     *
     * @see #inflate(Resources, XmlPullParser, AttributeSet, Theme)
     */
    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
            throws XmlPullParserException, IOException {
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
        inflate(r, parser, attrs, null);
    }

@@ -1192,17 +1196,11 @@ public abstract class Drawable {
     * @throws XmlPullParserException
     * @throws IOException
     */
    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        final TypedArray a;
        if (theme != null) {
            a = theme.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.Drawable, 0, 0);
        } else {
            a = r.obtainAttributes(attrs, com.android.internal.R.styleable.Drawable);
        }

        inflateWithAttributes(r, parser, a, com.android.internal.R.styleable.Drawable_visible);
        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);
        mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);
        a.recycle();
    }

@@ -1212,8 +1210,8 @@ public abstract class Drawable {
     * @throws XmlPullParserException
     * @throws IOException
     */
    void inflateWithAttributes(Resources r, XmlPullParser parser, TypedArray attrs, int visibleAttr)
            throws XmlPullParserException, IOException {
    void inflateWithAttributes(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull TypedArray attrs, int visibleAttr) throws XmlPullParserException, IOException {
        mVisible = attrs.getBoolean(visibleAttr, mVisible);
    }

+110 −14
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.graphics.drawable;

import com.android.internal.R;

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

@@ -23,6 +25,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -33,6 +36,7 @@ import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;

import java.io.IOException;
@@ -112,32 +116,79 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
        return mDrawable;
    }

    void updateStateFromTypedArray(TypedArray a) {
    @Override
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        super.inflate(r, parser, attrs, theme);

        final DrawableWrapperState state = mState;
        if (state == null) {
            return;
        }

        // Account for any configuration changes.
        state.mChangingConfigurations |= a.getChangingConfigurations();
        // The density may have changed since the last update. This will
        // apply scaling to any existing constant state properties.
        final int densityDpi = r.getDisplayMetrics().densityDpi;
        final int targetDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
        state.setDensity(targetDensity);

        // Extract the theme attributes, if any.
        state.mThemeAttrs = a.extractThemeAttrs();
        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.DrawableWrapper);
        updateStateFromTypedArray(a);
        a.recycle();

        // TODO: Consider using R.styleable.DrawableWrapper_drawable
        inflateChildDrawable(r, parser, attrs, theme);
    }

    @Override
    public void applyTheme(Resources.Theme t) {
    public void applyTheme(@NonNull Theme t) {
        super.applyTheme(t);

        // If we load the drawable later as part of updating from the typed
        // array, it will already be themed correctly. So, we can theme the
        // local drawable first.
        if (mDrawable != null && mDrawable.canApplyTheme()) {
            mDrawable.applyTheme(t);
        }

        final DrawableWrapperState state = mState;
        if (state == null) {
            return;
        }

        if (mDrawable != null && mDrawable.canApplyTheme()) {
            mDrawable.applyTheme(t);
        final int densityDpi = t.getResources().getDisplayMetrics().densityDpi;
        final int density = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
        state.setDensity(density);

        if (state.mThemeAttrs != null) {
            final TypedArray a = t.resolveAttributes(
                    state.mThemeAttrs, R.styleable.DrawableWrapper);
            updateStateFromTypedArray(a);
            a.recycle();
        }
    }

    /**
     * Updates constant state properties from the provided typed array.
     * <p>
     * Implementing subclasses should call through to the super method first.
     *
     * @param a the typed array rom which properties should be read
     */
    private void updateStateFromTypedArray(@NonNull TypedArray a) {
        final DrawableWrapperState state = mState;
        if (state == null) {
            return;
        }

        // Account for any configuration changes.
        state.mChangingConfigurations |= a.getChangingConfigurations();

        // Extract the theme attributes, if any.
        state.mThemeAttrs = a.extractThemeAttrs();

        if (a.hasValueOrEmpty(R.styleable.DrawableWrapper_drawable)) {
            setDrawable(a.getDrawable(R.styleable.DrawableWrapper_drawable));
        }
    }

@@ -371,8 +422,9 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
     * child element will take precedence over any other child elements or
     * explicit drawable attribute.
     */
    void inflateChildDrawable(Resources r, XmlPullParser parser, AttributeSet attrs,
            Resources.Theme theme) throws XmlPullParserException, IOException {
    private void inflateChildDrawable(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Theme theme)
            throws XmlPullParserException, IOException {
        // Seek to the first child element.
        Drawable dr = null;
        int type;
@@ -390,17 +442,61 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
    }

    abstract static class DrawableWrapperState extends Drawable.ConstantState {
        int[] mThemeAttrs;
        private int[] mThemeAttrs;

        int mChangingConfigurations;
        int mDensity = DisplayMetrics.DENSITY_DEFAULT;

        Drawable.ConstantState mDrawableState;

        DrawableWrapperState(DrawableWrapperState orig) {
        DrawableWrapperState(@Nullable DrawableWrapperState orig, @Nullable Resources res) {
            if (orig != null) {
                mThemeAttrs = orig.mThemeAttrs;
                mChangingConfigurations = orig.mChangingConfigurations;
                mDrawableState = orig.mDrawableState;
            }

            final int density;
            if (res != null) {
                density = res.getDisplayMetrics().densityDpi;
            } else if (orig != null) {
                density = orig.mDensity;
            } else {
                density = 0;
            }

            mDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density;
        }

        /**
         * Sets the constant state density.
         * <p>
         * If the density has been previously set, dispatches the change to
         * subclasses so that density-dependent properties may be scaled as
         * necessary.
         *
         * @param targetDensity the new constant state density
         */
        public final void setDensity(int targetDensity) {
            if (mDensity != targetDensity) {
                final int sourceDensity = mDensity;
                mDensity = targetDensity;

                onDensityChanged(sourceDensity, targetDensity);
            }
        }

        /**
         * Called when the constant state density changes.
         * <p>
         * Subclasses with density-dependent constant state properties should
         * override this method and scale their properties as necessary.
         *
         * @param sourceDensity the previous constant state density
         * @param targetDensity the new constant state density
         */
        void onDensityChanged(int sourceDensity, int targetDensity) {
            // Stub method.
        }

        @Override
@@ -425,7 +521,7 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb
        }

        @Override
        public abstract Drawable newDrawable(Resources res);
        public abstract Drawable newDrawable(@Nullable Resources res);

        @Override
        public int getChangingConfigurations() {
Loading