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

Commit 3da32b76 authored by Alan Viverette's avatar Alan Viverette
Browse files

Use floating-point value for Drawable level

This allows us to run fine-grained level animations.

Backwards compatibility:
Another CL will add DrawableCompat.setLevel(float) to forward calls to
the existing integer-based method. For callbacks, developers can override
onLevelChanged(int) and use DrawableCompat.getLevelFloat() to obtain the
floating-point level. Overriding onLevelChanged(float) will only work on
current API.

Bug: 23566299
Change-Id: I431fe6f3679c8f23f9cf3c2bb1f92a4059ee68e3
parent 3d21421a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -12302,6 +12302,7 @@ package android.graphics.drawable {
    method public int getIntrinsicWidth();
    method public int getLayoutDirection();
    method public final int getLevel();
    method public final float getLevelFloat();
    method public int getMinimumHeight();
    method public int getMinimumWidth();
    method public abstract int getOpacity();
@@ -12321,6 +12322,7 @@ package android.graphics.drawable {
    method protected void onBoundsChange(android.graphics.Rect);
    method public boolean onLayoutDirectionChanged(int);
    method protected boolean onLevelChange(int);
    method protected boolean onLevelChange(float);
    method protected boolean onStateChange(int[]);
    method public static int resolveOpacity(int, int);
    method public void scheduleSelf(java.lang.Runnable, long);
@@ -12338,12 +12340,15 @@ package android.graphics.drawable {
    method public void setHotspotBounds(int, int, int, int);
    method public final boolean setLayoutDirection(int);
    method public final boolean setLevel(int);
    method public final boolean setLevel(float);
    method public boolean setState(int[]);
    method public void setTint(int);
    method public void setTintList(android.content.res.ColorStateList);
    method public void setTintMode(android.graphics.PorterDuff.Mode);
    method public boolean setVisible(boolean, boolean);
    method public void unscheduleSelf(java.lang.Runnable);
    field public static final int MAX_LEVEL = 10000; // 0x2710
    field public static final float MAX_LEVEL_FLOAT = 10000.0f;
  }
  public static abstract interface Drawable.Callback {
+5 −0
Original line number Diff line number Diff line
@@ -12639,6 +12639,7 @@ package android.graphics.drawable {
    method public int getIntrinsicWidth();
    method public int getLayoutDirection();
    method public final int getLevel();
    method public final float getLevelFloat();
    method public int getMinimumHeight();
    method public int getMinimumWidth();
    method public abstract int getOpacity();
@@ -12658,6 +12659,7 @@ package android.graphics.drawable {
    method protected void onBoundsChange(android.graphics.Rect);
    method public boolean onLayoutDirectionChanged(int);
    method protected boolean onLevelChange(int);
    method protected boolean onLevelChange(float);
    method protected boolean onStateChange(int[]);
    method public static int resolveOpacity(int, int);
    method public void scheduleSelf(java.lang.Runnable, long);
@@ -12675,12 +12677,15 @@ package android.graphics.drawable {
    method public void setHotspotBounds(int, int, int, int);
    method public final boolean setLayoutDirection(int);
    method public final boolean setLevel(int);
    method public final boolean setLevel(float);
    method public boolean setState(int[]);
    method public void setTint(int);
    method public void setTintList(android.content.res.ColorStateList);
    method public void setTintMode(android.graphics.PorterDuff.Mode);
    method public boolean setVisible(boolean, boolean);
    method public void unscheduleSelf(java.lang.Runnable);
    field public static final int MAX_LEVEL = 10000; // 0x2710
    field public static final float MAX_LEVEL_FLOAT = 10000.0f;
  }
  public static abstract interface Drawable.Callback {
+1 −1
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
    }

    @Override
    protected boolean onLevelChange(int level) {
    protected boolean onLevelChange(float level) {
        return mAnimatedVectorState.mVectorDrawable.setLevel(level);
    }

+10 −12
Original line number Diff line number Diff line
@@ -52,8 +52,6 @@ public class ClipDrawable extends DrawableWrapper {
    public static final int HORIZONTAL = 1;
    public static final int VERTICAL = 2;

    private static final int MAX_LEVEL = 10000;

    private final Rect mTmpRect = new Rect();

    private ClipState mState;
@@ -143,7 +141,7 @@ public class ClipDrawable extends DrawableWrapper {
    }

    @Override
    protected boolean onLevelChange(int level) {
    protected boolean onLevelChange(float level) {
        super.onLevelChange(level);
        invalidateSelf();
        return true;
@@ -153,12 +151,12 @@ public class ClipDrawable extends DrawableWrapper {
    public int getOpacity() {
        final Drawable dr = getDrawable();
        final int opacity = dr.getOpacity();
        if (opacity == PixelFormat.TRANSPARENT || dr.getLevel() == 0) {
        if (opacity == PixelFormat.TRANSPARENT || dr.getLevelFloat() == 0) {
            return PixelFormat.TRANSPARENT;
        }

        final int level = getLevel();
        if (level >= MAX_LEVEL) {
        final float level = getLevelFloat();
        if (level >= MAX_LEVEL_FLOAT) {
            return dr.getOpacity();
        }

@@ -169,24 +167,24 @@ public class ClipDrawable extends DrawableWrapper {
    @Override
    public void draw(Canvas canvas) {
        final Drawable dr = getDrawable();
        if (dr.getLevel() == 0) {
        if (dr.getLevelFloat() == 0) {
            return;
        }

        final Rect r = mTmpRect;
        final Rect bounds = getBounds();
        final int level = getLevel();
        final float level = getLevelFloat();

        int w = bounds.width();
        final int iw = 0; //mState.mDrawable.getIntrinsicWidth();
        final int iw = 0;
        if ((mState.mOrientation & HORIZONTAL) != 0) {
            w -= (w - iw) * (MAX_LEVEL - level) / MAX_LEVEL;
            w -= Math.round((w - iw) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT);
        }

        int h = bounds.height();
        final int ih = 0; //mState.mDrawable.getIntrinsicHeight();
        final int ih = 0;
        if ((mState.mOrientation & VERTICAL) != 0) {
            h -= (h - ih) * (MAX_LEVEL - level) / MAX_LEVEL;
            h -= Math.round((h - ih) * (MAX_LEVEL_FLOAT - level) / MAX_LEVEL_FLOAT);
        }

        final int layoutDirection = getLayoutDirection();
+124 −23
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.graphics.Xfermode;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;
@@ -126,12 +127,19 @@ import java.util.Collection;
 * document.</p></div>
 */
public abstract class Drawable {

    private static final Rect ZERO_BOUNDS_RECT = new Rect();

    static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;

    /** The standard maximum value for calls to {@link #setLevel(int)}. */
    public static final int MAX_LEVEL = 10000;

    /** The standard maximum value for calls to {@link #setLevel(float)}. */
    public static final float MAX_LEVEL_FLOAT = 10000.0f;

    private int[] mStateSet = StateSet.WILD_CARD;
    private int mLevel = 0;
    private float mLevel = 0.0f;
    private int mChangingConfigurations = 0;
    private Rect mBounds = ZERO_BOUNDS_RECT;  // lazily becomes a new Rect()
    private WeakReference<Callback> mCallback = null;
@@ -711,22 +719,63 @@ public abstract class Drawable {
    }

    /**
     * Specify the level for the drawable.  This allows a drawable to vary its
     * imagery based on a continuous controller, for example to show progress
     * or volume level.
     *
     * <p>If the new level you are supplying causes the appearance of the
     * Drawable to change, then it is responsible for calling
     * {@link #invalidateSelf} in order to have itself redrawn, <em>and</em>
     * true will be returned from this function.
     *
     * @param level The new level, from 0 (minimum) to 10000 (maximum).
     * Sets the level for the drawable as an integer value where typically the
     * minimum level is 0 and the maximum is 10000 {@link #MAX_LEVEL}; however,
     * the range may vary based on the Drawable implementation and is not
     * clamped.
     * <p>
     * This allows a drawable to vary its imagery based on a continuous
     * controller. For example, it may be used to show progress or volume
     * level.
     * <p>
     * Use #setLevelFloat(float) to set the level as a high-precision
     * floating-point value.
     *
     * @return Returns true if this change in level has caused the appearance
     * of the Drawable to change (hence requiring an invalidate), otherwise
     * returns false.
     * @param level the new level, typically between 0 and 10000
     * @return {@code true} if this change in level has caused the appearance
     *         of the drawable to change and will require a subsequent call to
     *         invalidate, {@code false} otherwise
     * @see #getLevel()
     * @see #setLevel(float)
     * @see #onLevelChange(int)
     */
    public final boolean setLevel(int level) {
        return setLevel((float) level);
    }

    /**
     * Returns the current level as a rounded integer value.
     * <p>
     * Use #getLevelFloat() to return the level as a high-precision
     * floating-point value.
     *
     * @return the current level, typically between 0 and 10000
     * @see #setLevel(int)
     * @see #getLevelFloat()
     */
    public final int getLevel() {
        return Math.round(mLevel);
    }

    /**
     * Sets the level for the drawable as a floating-point value where
     * typically the minimum level is 0.0 and the maximum is 10000.0
     * {@link #MAX_LEVEL_FLOAT}; however, the range may vary based on the
     * Drawable implementation and is not clamped.
     * <p>
     * This allows a drawable to vary its imagery based on a continuous
     * controller. For example, it may be used to show progress or volume
     * level.
     *
     * @param level the new level, typically between 0.0 and 10000.0
     *              ({@link #MAX_LEVEL_FLOAT})
     * @return {@code true} if this change in level has caused the appearance
     *         of the drawable to change and will require a subsequent call to
     *         invalidate, {@code false} otherwise
     * @see #getLevelFloat()
     * @see #onLevelChange(float)
     */
    public final boolean setLevel(float level) {
        if (mLevel != level) {
            mLevel = level;
            return onLevelChange(level);
@@ -735,11 +784,13 @@ public abstract class Drawable {
    }

    /**
     * Retrieve the current level.
     * Returns the current level as a floating-point value.
     *
     * @return int Current level, from 0 (minimum) to 10000 (maximum).
     * @return the current level, typically between 0.0 and 10000.0
     *         ({@link #MAX_LEVEL_FLOAT})
     * @see #setLevel(float)
     */
    public final int getLevel() {
    public final float getLevelFloat() {
        return mLevel;
    }

@@ -894,14 +945,47 @@ public abstract class Drawable {
     * last state.
     */
    protected boolean onStateChange(int[] state) { return false; }
    /** Override this in your subclass to change appearance if you vary based
     *  on level.
     * @return Returns true if the level change has caused the appearance of
     * the Drawable to change (that is, it needs to be drawn), else false
     * if it looks the same and there is no need to redraw it since its
     * last level.

    /**
     * Called when the drawable level changes.
     * <p>
     * Override this in your subclass to change appearance if you vary based on
     * level and do not need floating-point accuracy. To handle changes with
     * higher accuracy, override {@link #onLevelChange(float)} instead.
     * <p>
     * <strong>Note:</strong> Do not override both this method and
     * {@link #onLevelChange(float)}. Only override one method.
     *
     * @param level the level as an integer value, typically between 0
     *              (minimum) and 10000 ({@link #MAX_LEVEL})
     * @return {@code true} if the level change has caused the appearance of
     *         the drawable to change such that it needs to be redrawn, or
     *         {@code false} if there is no need to redraw
     */
    protected boolean onLevelChange(int level) { return false; }

    /**
     * Called when the drawable level changes.
     * <p>
     * Override this in your subclass to change appearance if you vary based on
     * level and need floating-point accuracy.
     * <p>
     * <strong>Note:</strong> Do not override both this method and
     * {@link #onLevelChange(int)}. Only override one method. If your app
     * targets SDK <= 23 ({@link android.os.Build.VERSION_CODES#M M}), you
     * will need to override {@link #onLevelChange(int)} to receive callbacks
     * on devices running Android M and below.
     *
     * @param level the level as a floating-point value, typically between 0.0
     *              and 10000.0 ({@link #MAX_LEVEL_FLOAT})
     * @return {@code true} if the level change has caused the appearance of
     *         the drawable to change such that it needs to be redrawn, or
     *         {@code false} if there is no need to redraw
     */
    protected boolean onLevelChange(float level) {
        return onLevelChange(Math.round(level));
    }

    /**
     * Override this in your subclass to change appearance if you vary based on
     * the bounds.
@@ -1327,6 +1411,23 @@ public abstract class Drawable {
        return tintFilter;
    }

    /**
     * Animatable property for Drawable level.
     *
     * @hide Until Drawable animations have been cleaned up.
     */
    public static final FloatProperty<Drawable> LEVEL = new FloatProperty<Drawable>("levelFloat") {
        @Override
        public Float get(Drawable object) {
            return object.getLevelFloat();
        }

        @Override
        public void setValue(Drawable object, float value) {
            object.setLevel(value);
        }
    };

    /**
     * Obtains styled attributes from the theme, if available, or unstyled
     * resources if the theme is null.
Loading