Loading core/java/android/view/RenderNodeAnimator.java +3 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; Loading Loading @@ -281,12 +280,9 @@ public class RenderNodeAnimator extends Animator { setTarget(mViewTarget.mRenderNode); } public void setTarget(Canvas canvas) { if (!(canvas instanceof DisplayListCanvas)) { throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); } final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas; setTarget(recordingCanvas.mNode); /** Sets the animation target to the owning view of the DisplayListCanvas */ public void setTarget(DisplayListCanvas canvas) { setTarget(canvas.mNode); } private void setTarget(RenderNode node) { Loading graphics/java/android/graphics/drawable/RippleBackground.java +34 −103 Original line number Diff line number Diff line Loading @@ -36,138 +36,69 @@ class RippleBackground extends RippleComponent { private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final int OPACITY_ENTER_DURATION = 600; private static final int OPACITY_ENTER_DURATION_FAST = 120; private static final int OPACITY_EXIT_DURATION = 480; private static final int OPACITY_DURATION = 80; // Hardware rendering properties. private CanvasProperty<Paint> mPropPaint; private CanvasProperty<Float> mPropRadius; private CanvasProperty<Float> mPropX; private CanvasProperty<Float> mPropY; private ObjectAnimator mAnimator; // Software rendering properties. private float mOpacity = 0; /** Whether this ripple is bounded. */ private boolean mIsBounded; public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded, boolean forceSoftware) { super(owner, bounds, forceSoftware); private boolean mFocused = false; private boolean mHovered = false; public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) { super(owner, bounds); mIsBounded = isBounded; } public boolean isVisible() { return mOpacity > 0 || isHardwareAnimating(); return mOpacity > 0; } @Override protected boolean drawSoftware(Canvas c, Paint p) { boolean hasContent = false; public void draw(Canvas c, Paint p) { final int origAlpha = p.getAlpha(); final int alpha = (int) (origAlpha * mOpacity + 0.5f); final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255); if (alpha > 0) { p.setAlpha(alpha); c.drawCircle(0, 0, mTargetRadius, p); p.setAlpha(origAlpha); hasContent = true; } return hasContent; } @Override protected boolean drawHardware(DisplayListCanvas c) { c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); return true; public void setState(boolean focused, boolean hovered, boolean animateChanged) { if (mHovered != hovered || mFocused != focused) { mHovered = hovered; mFocused = focused; onStateChanged(animateChanged); } @Override protected Animator createSoftwareEnter(boolean fast) { // Linear enter based on current opacity. final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION; final int duration = (int) ((1 - mOpacity) * maxDuration); final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); opacity.setAutoCancel(true); opacity.setDuration(duration); opacity.setInterpolator(LINEAR_INTERPOLATOR); return opacity; } @Override protected Animator createSoftwareExit() { final AnimatorSet set = new AnimatorSet(); // Linear exit after enter is completed. final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0); exit.setInterpolator(LINEAR_INTERPOLATOR); exit.setDuration(OPACITY_EXIT_DURATION); exit.setAutoCancel(true); final AnimatorSet.Builder builder = set.play(exit); // Linear "fast" enter based on current opacity. final int fastEnterDuration = mIsBounded ? (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; if (fastEnterDuration > 0) { final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1); enter.setInterpolator(LINEAR_INTERPOLATOR); enter.setDuration(fastEnterDuration); enter.setAutoCancel(true); builder.after(enter); } return set; private void onStateChanged(boolean animateChanged) { float newOpacity = 0.0f; if (mHovered) newOpacity += 1.0f; if (mFocused) newOpacity += 1.0f; if (mAnimator != null) { mAnimator.cancel(); mAnimator = null; } @Override protected RenderNodeAnimatorSet createHardwareExit(Paint p) { final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet(); final int targetAlpha = p.getAlpha(); final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f); p.setAlpha(currentAlpha); mPropPaint = CanvasProperty.createPaint(p); mPropRadius = CanvasProperty.createFloat(mTargetRadius); mPropX = CanvasProperty.createFloat(0); mPropY = CanvasProperty.createFloat(0); final int fastEnterDuration = mIsBounded ? (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; // Linear exit after enter is completed. final RenderNodeAnimator exit = new RenderNodeAnimator( mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0); exit.setInterpolator(LINEAR_INTERPOLATOR); exit.setDuration(OPACITY_EXIT_DURATION); if (fastEnterDuration > 0) { exit.setStartDelay(fastEnterDuration); exit.setStartValue(targetAlpha); if (animateChanged) { mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity); mAnimator.setDuration(OPACITY_DURATION); mAnimator.setInterpolator(LINEAR_INTERPOLATOR); mAnimator.start(); } else { mOpacity = newOpacity; } set.add(exit); // Linear "fast" enter based on current opacity. if (fastEnterDuration > 0) { final RenderNodeAnimator enter = new RenderNodeAnimator( mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); enter.setInterpolator(LINEAR_INTERPOLATOR); enter.setDuration(fastEnterDuration); set.add(enter); } return set; public void jumpToFinal() { if (mAnimator != null) { mAnimator.end(); mAnimator = null; } @Override protected void jumpValuesToExit() { mOpacity = 0; } private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> { Loading graphics/java/android/graphics/drawable/RippleComponent.java +3 −241 Original line number Diff line number Diff line Loading @@ -27,23 +27,14 @@ import android.view.RenderNodeAnimator; import java.util.ArrayList; /** * Abstract class that handles hardware/software hand-off and lifecycle for * animated ripple foreground and background components. * Abstract class that handles size & positioning common to the ripple & focus states. */ abstract class RippleComponent { private final RippleDrawable mOwner; protected final RippleDrawable mOwner; /** Bounds used for computing max radius. May be modified by the owner. */ protected final Rect mBounds; /** Whether we can use hardware acceleration for the exit animation. */ private boolean mHasDisplayListCanvas; private boolean mHasPendingHardwareAnimator; private RenderNodeAnimatorSet mHardwareAnimator; private Animator mSoftwareAnimator; /** Whether we have an explicit maximum radius. */ private boolean mHasMaxRadius; Loading @@ -53,16 +44,9 @@ abstract class RippleComponent { /** Screen density used to adjust pixel-based constants. */ protected float mDensityScale; /** * If set, force all ripple animations to not run on RenderThread, even if it would be * available. */ private final boolean mForceSoftware; public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) { public RippleComponent(RippleDrawable owner, Rect bounds) { mOwner = owner; mBounds = bounds; mForceSoftware = forceSoftware; } public void onBoundsChange() { Loading Loading @@ -91,89 +75,6 @@ abstract class RippleComponent { return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); } /** * Starts a ripple enter animation. * * @param fast whether the ripple should enter quickly */ public final void enter(boolean fast) { cancel(); mSoftwareAnimator = createSoftwareEnter(fast); if (mSoftwareAnimator != null) { mSoftwareAnimator.start(); } } /** * Starts a ripple exit animation. */ public final void exit() { cancel(); if (mHasDisplayListCanvas) { // We don't have access to a canvas here, but we expect one on the // next frame. We'll start the render thread animation then. mHasPendingHardwareAnimator = true; // Request another frame. invalidateSelf(); } else { mSoftwareAnimator = createSoftwareExit(); mSoftwareAnimator.start(); } } /** * Cancels all animations. Software animation values are left in the * current state, while hardware animation values jump to the end state. */ public void cancel() { cancelSoftwareAnimations(); endHardwareAnimations(); } /** * Ends all animations, jumping values to the end state. */ public void end() { endSoftwareAnimations(); endHardwareAnimations(); } /** * Draws the ripple to the canvas, inheriting the paint's color and alpha * properties. * * @param c the canvas to which the ripple should be drawn * @param p the paint used to draw the ripple * @return {@code true} if something was drawn, {@code false} otherwise */ public boolean draw(Canvas c, Paint p) { final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated() && c instanceof DisplayListCanvas; if (mHasDisplayListCanvas != hasDisplayListCanvas) { mHasDisplayListCanvas = hasDisplayListCanvas; if (!hasDisplayListCanvas) { // We've switched from hardware to non-hardware mode. Panic. endHardwareAnimations(); } } if (hasDisplayListCanvas) { final DisplayListCanvas hw = (DisplayListCanvas) c; startPendingAnimation(hw, p); if (mHardwareAnimator != null) { return drawHardware(hw); } } return drawSoftware(c, p); } /** * Populates {@code bounds} with the maximum drawing bounds of the ripple * relative to its center. The resulting bounds should be translated into Loading @@ -186,77 +87,10 @@ abstract class RippleComponent { bounds.set(-r, -r, r, r); } /** * Starts the pending hardware animation, if available. * * @param hw hardware canvas on which the animation should draw * @param p paint whose properties the hardware canvas should use */ private void startPendingAnimation(DisplayListCanvas hw, Paint p) { if (mHasPendingHardwareAnimator) { mHasPendingHardwareAnimator = false; mHardwareAnimator = createHardwareExit(new Paint(p)); mHardwareAnimator.start(hw); // Preemptively jump the software values to the end state now that // the hardware exit has read whatever values it needs. jumpValuesToExit(); } } /** * Cancels any current software animations, leaving the values in their * current state. */ private void cancelSoftwareAnimations() { if (mSoftwareAnimator != null) { mSoftwareAnimator.cancel(); mSoftwareAnimator = null; } } /** * Ends any current software animations, jumping the values to their end * state. */ private void endSoftwareAnimations() { if (mSoftwareAnimator != null) { mSoftwareAnimator.end(); mSoftwareAnimator = null; } } /** * Ends any pending or current hardware animations. * <p> * Hardware animations can't synchronize values back to the software * thread, so there is no "cancel" equivalent. */ private void endHardwareAnimations() { if (mHardwareAnimator != null) { mHardwareAnimator.end(); mHardwareAnimator = null; } if (mHasPendingHardwareAnimator) { mHasPendingHardwareAnimator = false; // Manually jump values to their exited state. Normally we'd do that // later when starting the hardware exit, but we're aborting early. jumpValuesToExit(); } } protected final void invalidateSelf() { mOwner.invalidateSelf(false); } protected final boolean isHardwareAnimating() { return mHardwareAnimator != null && mHardwareAnimator.isRunning() || mHasPendingHardwareAnimator; } protected final void onHotspotBoundsChanged() { if (!mHasMaxRadius) { final float halfWidth = mBounds.width() / 2.0f; Loading @@ -276,76 +110,4 @@ abstract class RippleComponent { protected void onTargetRadiusChanged(float targetRadius) { // Stub. } protected abstract Animator createSoftwareEnter(boolean fast); protected abstract Animator createSoftwareExit(); protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p); protected abstract boolean drawHardware(DisplayListCanvas c); protected abstract boolean drawSoftware(Canvas c, Paint p); /** * Called when the hardware exit is cancelled. Jumps software values to end * state to ensure that software and hardware values are synchronized. */ protected abstract void jumpValuesToExit(); public static class RenderNodeAnimatorSet { private final ArrayList<RenderNodeAnimator> mAnimators = new ArrayList<>(); public void add(RenderNodeAnimator anim) { mAnimators.add(anim); } public void clear() { mAnimators.clear(); } public void start(DisplayListCanvas target) { if (target == null) { throw new IllegalArgumentException("Hardware canvas must be non-null"); } final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.setTarget(target); anim.start(); } } public void cancel() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.cancel(); } } public void end() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.end(); } } public boolean isRunning() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); if (anim.isRunning()) { return true; } } return false; } } } graphics/java/android/graphics/drawable/RippleDrawable.java +53 −77 Original line number Diff line number Diff line Loading @@ -16,11 +16,6 @@ package android.graphics.drawable; 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.pm.ActivityInfo.Config; Loading @@ -42,6 +37,11 @@ import android.graphics.Rect; import android.graphics.Shader; import android.util.AttributeSet; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Arrays; Loading Loading @@ -135,9 +135,6 @@ public class RippleDrawable extends LayerDrawable { private PorterDuffColorFilter mMaskColorFilter; private boolean mHasValidMask; /** Whether we expect to draw a background when visible. */ private boolean mBackgroundActive; /** The current ripple. May be actively animating or pending entry. */ private RippleForeground mRipple; Loading Loading @@ -217,7 +214,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { mBackground.end(); mBackground.jumpToFinal(); } cancelExitingRipples(); Loading Loading @@ -266,9 +263,9 @@ public class RippleDrawable extends LayerDrawable { } } setRippleActive(focused || (enabled && pressed)); setRippleActive(enabled && pressed); setBackgroundActive(hovered, hovered); setBackgroundActive(hovered, focused); return changed; } Loading @@ -283,14 +280,13 @@ public class RippleDrawable extends LayerDrawable { } } private void setBackgroundActive(boolean active, boolean focused) { if (mBackgroundActive != active) { mBackgroundActive = active; if (active) { tryBackgroundEnter(focused); } else { tryBackgroundExit(); private void setBackgroundActive(boolean hovered, boolean focused) { if (mBackground == null && (hovered || focused)) { mBackground = new RippleBackground(this, mHotspotBounds, isBounded()); mBackground.setup(mState.mMaxRadius, mDensity); } if (mBackground != null) { mBackground.setState(focused, hovered, true); } } Loading Loading @@ -327,10 +323,6 @@ public class RippleDrawable extends LayerDrawable { tryRippleEnter(); } if (mBackgroundActive) { tryBackgroundEnter(false); } // Skip animations, just show the correct final states. jumpToCurrentState(); } Loading Loading @@ -545,26 +537,6 @@ public class RippleDrawable extends LayerDrawable { } } /** * Creates an active hotspot at the specified location. */ private void tryBackgroundEnter(boolean focused) { if (mBackground == null) { final boolean isBounded = isBounded(); mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware); } mBackground.setup(mState.mMaxRadius, mDensity); mBackground.enter(focused); } private void tryBackgroundExit() { if (mBackground != null) { // Don't null out the background, we need it to draw! mBackground.exit(); } } /** * Attempts to start an enter animation for the active hotspot. Fails if * there are too many animating ripples. Loading Loading @@ -593,7 +565,7 @@ public class RippleDrawable extends LayerDrawable { } mRipple.setup(mState.mMaxRadius, mDensity); mRipple.enter(false); mRipple.enter(); } /** Loading Loading @@ -623,9 +595,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { mBackground.end(); mBackground = null; mBackgroundActive = false; mBackground.setState(false, false, false); } cancelExitingRipples(); Loading Loading @@ -858,6 +828,40 @@ public class RippleDrawable extends LayerDrawable { final float y = mHotspotBounds.exactCenterY(); canvas.translate(x, y); final Paint p = getRipplePaint(); if (background != null && background.isVisible()) { background.draw(canvas, p); } if (count > 0) { final RippleForeground[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { ripples[i].draw(canvas, p); } } if (active != null) { active.draw(canvas, p); } canvas.translate(-x, -y); } private void drawMask(Canvas canvas) { mMask.draw(canvas); } Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); } final float x = mHotspotBounds.exactCenterX(); final float y = mHotspotBounds.exactCenterY(); updateMaskShaderIfNeeded(); // Position the shader to account for canvas translation. Loading @@ -871,7 +875,7 @@ public class RippleDrawable extends LayerDrawable { // half so that the ripple and background together yield full alpha. final int color = mState.mColor.getColorForState(getState(), Color.BLACK); final int halfAlpha = (Color.alpha(color) / 2) << 24; final Paint p = getRipplePaint(); final Paint p = mRipplePaint; if (mMaskColorFilter != null) { // The ripple timing depends on the paint's alpha value, so we need Loading @@ -890,35 +894,7 @@ public class RippleDrawable extends LayerDrawable { p.setShader(null); } if (background != null && background.isVisible()) { background.draw(canvas, p); } if (count > 0) { final RippleForeground[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { ripples[i].draw(canvas, p); } } if (active != null) { active.draw(canvas, p); } canvas.translate(-x, -y); } private void drawMask(Canvas canvas) { mMask.draw(canvas); } private Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); } return mRipplePaint; return p; } @Override Loading graphics/java/android/graphics/drawable/RippleForeground.java +218 −126 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/view/RenderNodeAnimator.java +3 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; Loading Loading @@ -281,12 +280,9 @@ public class RenderNodeAnimator extends Animator { setTarget(mViewTarget.mRenderNode); } public void setTarget(Canvas canvas) { if (!(canvas instanceof DisplayListCanvas)) { throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); } final DisplayListCanvas recordingCanvas = (DisplayListCanvas) canvas; setTarget(recordingCanvas.mNode); /** Sets the animation target to the owning view of the DisplayListCanvas */ public void setTarget(DisplayListCanvas canvas) { setTarget(canvas.mNode); } private void setTarget(RenderNode node) { Loading
graphics/java/android/graphics/drawable/RippleBackground.java +34 −103 Original line number Diff line number Diff line Loading @@ -36,138 +36,69 @@ class RippleBackground extends RippleComponent { private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final int OPACITY_ENTER_DURATION = 600; private static final int OPACITY_ENTER_DURATION_FAST = 120; private static final int OPACITY_EXIT_DURATION = 480; private static final int OPACITY_DURATION = 80; // Hardware rendering properties. private CanvasProperty<Paint> mPropPaint; private CanvasProperty<Float> mPropRadius; private CanvasProperty<Float> mPropX; private CanvasProperty<Float> mPropY; private ObjectAnimator mAnimator; // Software rendering properties. private float mOpacity = 0; /** Whether this ripple is bounded. */ private boolean mIsBounded; public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded, boolean forceSoftware) { super(owner, bounds, forceSoftware); private boolean mFocused = false; private boolean mHovered = false; public RippleBackground(RippleDrawable owner, Rect bounds, boolean isBounded) { super(owner, bounds); mIsBounded = isBounded; } public boolean isVisible() { return mOpacity > 0 || isHardwareAnimating(); return mOpacity > 0; } @Override protected boolean drawSoftware(Canvas c, Paint p) { boolean hasContent = false; public void draw(Canvas c, Paint p) { final int origAlpha = p.getAlpha(); final int alpha = (int) (origAlpha * mOpacity + 0.5f); final int alpha = Math.min((int) (origAlpha * mOpacity + 0.5f), 255); if (alpha > 0) { p.setAlpha(alpha); c.drawCircle(0, 0, mTargetRadius, p); p.setAlpha(origAlpha); hasContent = true; } return hasContent; } @Override protected boolean drawHardware(DisplayListCanvas c) { c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); return true; public void setState(boolean focused, boolean hovered, boolean animateChanged) { if (mHovered != hovered || mFocused != focused) { mHovered = hovered; mFocused = focused; onStateChanged(animateChanged); } @Override protected Animator createSoftwareEnter(boolean fast) { // Linear enter based on current opacity. final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION; final int duration = (int) ((1 - mOpacity) * maxDuration); final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); opacity.setAutoCancel(true); opacity.setDuration(duration); opacity.setInterpolator(LINEAR_INTERPOLATOR); return opacity; } @Override protected Animator createSoftwareExit() { final AnimatorSet set = new AnimatorSet(); // Linear exit after enter is completed. final ObjectAnimator exit = ObjectAnimator.ofFloat(this, OPACITY, 0); exit.setInterpolator(LINEAR_INTERPOLATOR); exit.setDuration(OPACITY_EXIT_DURATION); exit.setAutoCancel(true); final AnimatorSet.Builder builder = set.play(exit); // Linear "fast" enter based on current opacity. final int fastEnterDuration = mIsBounded ? (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; if (fastEnterDuration > 0) { final ObjectAnimator enter = ObjectAnimator.ofFloat(this, OPACITY, 1); enter.setInterpolator(LINEAR_INTERPOLATOR); enter.setDuration(fastEnterDuration); enter.setAutoCancel(true); builder.after(enter); } return set; private void onStateChanged(boolean animateChanged) { float newOpacity = 0.0f; if (mHovered) newOpacity += 1.0f; if (mFocused) newOpacity += 1.0f; if (mAnimator != null) { mAnimator.cancel(); mAnimator = null; } @Override protected RenderNodeAnimatorSet createHardwareExit(Paint p) { final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet(); final int targetAlpha = p.getAlpha(); final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f); p.setAlpha(currentAlpha); mPropPaint = CanvasProperty.createPaint(p); mPropRadius = CanvasProperty.createFloat(mTargetRadius); mPropX = CanvasProperty.createFloat(0); mPropY = CanvasProperty.createFloat(0); final int fastEnterDuration = mIsBounded ? (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST) : 0; // Linear exit after enter is completed. final RenderNodeAnimator exit = new RenderNodeAnimator( mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0); exit.setInterpolator(LINEAR_INTERPOLATOR); exit.setDuration(OPACITY_EXIT_DURATION); if (fastEnterDuration > 0) { exit.setStartDelay(fastEnterDuration); exit.setStartValue(targetAlpha); if (animateChanged) { mAnimator = ObjectAnimator.ofFloat(this, OPACITY, newOpacity); mAnimator.setDuration(OPACITY_DURATION); mAnimator.setInterpolator(LINEAR_INTERPOLATOR); mAnimator.start(); } else { mOpacity = newOpacity; } set.add(exit); // Linear "fast" enter based on current opacity. if (fastEnterDuration > 0) { final RenderNodeAnimator enter = new RenderNodeAnimator( mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); enter.setInterpolator(LINEAR_INTERPOLATOR); enter.setDuration(fastEnterDuration); set.add(enter); } return set; public void jumpToFinal() { if (mAnimator != null) { mAnimator.end(); mAnimator = null; } @Override protected void jumpValuesToExit() { mOpacity = 0; } private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> { Loading
graphics/java/android/graphics/drawable/RippleComponent.java +3 −241 Original line number Diff line number Diff line Loading @@ -27,23 +27,14 @@ import android.view.RenderNodeAnimator; import java.util.ArrayList; /** * Abstract class that handles hardware/software hand-off and lifecycle for * animated ripple foreground and background components. * Abstract class that handles size & positioning common to the ripple & focus states. */ abstract class RippleComponent { private final RippleDrawable mOwner; protected final RippleDrawable mOwner; /** Bounds used for computing max radius. May be modified by the owner. */ protected final Rect mBounds; /** Whether we can use hardware acceleration for the exit animation. */ private boolean mHasDisplayListCanvas; private boolean mHasPendingHardwareAnimator; private RenderNodeAnimatorSet mHardwareAnimator; private Animator mSoftwareAnimator; /** Whether we have an explicit maximum radius. */ private boolean mHasMaxRadius; Loading @@ -53,16 +44,9 @@ abstract class RippleComponent { /** Screen density used to adjust pixel-based constants. */ protected float mDensityScale; /** * If set, force all ripple animations to not run on RenderThread, even if it would be * available. */ private final boolean mForceSoftware; public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) { public RippleComponent(RippleDrawable owner, Rect bounds) { mOwner = owner; mBounds = bounds; mForceSoftware = forceSoftware; } public void onBoundsChange() { Loading Loading @@ -91,89 +75,6 @@ abstract class RippleComponent { return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); } /** * Starts a ripple enter animation. * * @param fast whether the ripple should enter quickly */ public final void enter(boolean fast) { cancel(); mSoftwareAnimator = createSoftwareEnter(fast); if (mSoftwareAnimator != null) { mSoftwareAnimator.start(); } } /** * Starts a ripple exit animation. */ public final void exit() { cancel(); if (mHasDisplayListCanvas) { // We don't have access to a canvas here, but we expect one on the // next frame. We'll start the render thread animation then. mHasPendingHardwareAnimator = true; // Request another frame. invalidateSelf(); } else { mSoftwareAnimator = createSoftwareExit(); mSoftwareAnimator.start(); } } /** * Cancels all animations. Software animation values are left in the * current state, while hardware animation values jump to the end state. */ public void cancel() { cancelSoftwareAnimations(); endHardwareAnimations(); } /** * Ends all animations, jumping values to the end state. */ public void end() { endSoftwareAnimations(); endHardwareAnimations(); } /** * Draws the ripple to the canvas, inheriting the paint's color and alpha * properties. * * @param c the canvas to which the ripple should be drawn * @param p the paint used to draw the ripple * @return {@code true} if something was drawn, {@code false} otherwise */ public boolean draw(Canvas c, Paint p) { final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated() && c instanceof DisplayListCanvas; if (mHasDisplayListCanvas != hasDisplayListCanvas) { mHasDisplayListCanvas = hasDisplayListCanvas; if (!hasDisplayListCanvas) { // We've switched from hardware to non-hardware mode. Panic. endHardwareAnimations(); } } if (hasDisplayListCanvas) { final DisplayListCanvas hw = (DisplayListCanvas) c; startPendingAnimation(hw, p); if (mHardwareAnimator != null) { return drawHardware(hw); } } return drawSoftware(c, p); } /** * Populates {@code bounds} with the maximum drawing bounds of the ripple * relative to its center. The resulting bounds should be translated into Loading @@ -186,77 +87,10 @@ abstract class RippleComponent { bounds.set(-r, -r, r, r); } /** * Starts the pending hardware animation, if available. * * @param hw hardware canvas on which the animation should draw * @param p paint whose properties the hardware canvas should use */ private void startPendingAnimation(DisplayListCanvas hw, Paint p) { if (mHasPendingHardwareAnimator) { mHasPendingHardwareAnimator = false; mHardwareAnimator = createHardwareExit(new Paint(p)); mHardwareAnimator.start(hw); // Preemptively jump the software values to the end state now that // the hardware exit has read whatever values it needs. jumpValuesToExit(); } } /** * Cancels any current software animations, leaving the values in their * current state. */ private void cancelSoftwareAnimations() { if (mSoftwareAnimator != null) { mSoftwareAnimator.cancel(); mSoftwareAnimator = null; } } /** * Ends any current software animations, jumping the values to their end * state. */ private void endSoftwareAnimations() { if (mSoftwareAnimator != null) { mSoftwareAnimator.end(); mSoftwareAnimator = null; } } /** * Ends any pending or current hardware animations. * <p> * Hardware animations can't synchronize values back to the software * thread, so there is no "cancel" equivalent. */ private void endHardwareAnimations() { if (mHardwareAnimator != null) { mHardwareAnimator.end(); mHardwareAnimator = null; } if (mHasPendingHardwareAnimator) { mHasPendingHardwareAnimator = false; // Manually jump values to their exited state. Normally we'd do that // later when starting the hardware exit, but we're aborting early. jumpValuesToExit(); } } protected final void invalidateSelf() { mOwner.invalidateSelf(false); } protected final boolean isHardwareAnimating() { return mHardwareAnimator != null && mHardwareAnimator.isRunning() || mHasPendingHardwareAnimator; } protected final void onHotspotBoundsChanged() { if (!mHasMaxRadius) { final float halfWidth = mBounds.width() / 2.0f; Loading @@ -276,76 +110,4 @@ abstract class RippleComponent { protected void onTargetRadiusChanged(float targetRadius) { // Stub. } protected abstract Animator createSoftwareEnter(boolean fast); protected abstract Animator createSoftwareExit(); protected abstract RenderNodeAnimatorSet createHardwareExit(Paint p); protected abstract boolean drawHardware(DisplayListCanvas c); protected abstract boolean drawSoftware(Canvas c, Paint p); /** * Called when the hardware exit is cancelled. Jumps software values to end * state to ensure that software and hardware values are synchronized. */ protected abstract void jumpValuesToExit(); public static class RenderNodeAnimatorSet { private final ArrayList<RenderNodeAnimator> mAnimators = new ArrayList<>(); public void add(RenderNodeAnimator anim) { mAnimators.add(anim); } public void clear() { mAnimators.clear(); } public void start(DisplayListCanvas target) { if (target == null) { throw new IllegalArgumentException("Hardware canvas must be non-null"); } final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.setTarget(target); anim.start(); } } public void cancel() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.cancel(); } } public void end() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); anim.end(); } } public boolean isRunning() { final ArrayList<RenderNodeAnimator> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final RenderNodeAnimator anim = animators.get(i); if (anim.isRunning()) { return true; } } return false; } } }
graphics/java/android/graphics/drawable/RippleDrawable.java +53 −77 Original line number Diff line number Diff line Loading @@ -16,11 +16,6 @@ package android.graphics.drawable; 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.pm.ActivityInfo.Config; Loading @@ -42,6 +37,11 @@ import android.graphics.Rect; import android.graphics.Shader; import android.util.AttributeSet; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Arrays; Loading Loading @@ -135,9 +135,6 @@ public class RippleDrawable extends LayerDrawable { private PorterDuffColorFilter mMaskColorFilter; private boolean mHasValidMask; /** Whether we expect to draw a background when visible. */ private boolean mBackgroundActive; /** The current ripple. May be actively animating or pending entry. */ private RippleForeground mRipple; Loading Loading @@ -217,7 +214,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { mBackground.end(); mBackground.jumpToFinal(); } cancelExitingRipples(); Loading Loading @@ -266,9 +263,9 @@ public class RippleDrawable extends LayerDrawable { } } setRippleActive(focused || (enabled && pressed)); setRippleActive(enabled && pressed); setBackgroundActive(hovered, hovered); setBackgroundActive(hovered, focused); return changed; } Loading @@ -283,14 +280,13 @@ public class RippleDrawable extends LayerDrawable { } } private void setBackgroundActive(boolean active, boolean focused) { if (mBackgroundActive != active) { mBackgroundActive = active; if (active) { tryBackgroundEnter(focused); } else { tryBackgroundExit(); private void setBackgroundActive(boolean hovered, boolean focused) { if (mBackground == null && (hovered || focused)) { mBackground = new RippleBackground(this, mHotspotBounds, isBounded()); mBackground.setup(mState.mMaxRadius, mDensity); } if (mBackground != null) { mBackground.setState(focused, hovered, true); } } Loading Loading @@ -327,10 +323,6 @@ public class RippleDrawable extends LayerDrawable { tryRippleEnter(); } if (mBackgroundActive) { tryBackgroundEnter(false); } // Skip animations, just show the correct final states. jumpToCurrentState(); } Loading Loading @@ -545,26 +537,6 @@ public class RippleDrawable extends LayerDrawable { } } /** * Creates an active hotspot at the specified location. */ private void tryBackgroundEnter(boolean focused) { if (mBackground == null) { final boolean isBounded = isBounded(); mBackground = new RippleBackground(this, mHotspotBounds, isBounded, mForceSoftware); } mBackground.setup(mState.mMaxRadius, mDensity); mBackground.enter(focused); } private void tryBackgroundExit() { if (mBackground != null) { // Don't null out the background, we need it to draw! mBackground.exit(); } } /** * Attempts to start an enter animation for the active hotspot. Fails if * there are too many animating ripples. Loading Loading @@ -593,7 +565,7 @@ public class RippleDrawable extends LayerDrawable { } mRipple.setup(mState.mMaxRadius, mDensity); mRipple.enter(false); mRipple.enter(); } /** Loading Loading @@ -623,9 +595,7 @@ public class RippleDrawable extends LayerDrawable { } if (mBackground != null) { mBackground.end(); mBackground = null; mBackgroundActive = false; mBackground.setState(false, false, false); } cancelExitingRipples(); Loading Loading @@ -858,6 +828,40 @@ public class RippleDrawable extends LayerDrawable { final float y = mHotspotBounds.exactCenterY(); canvas.translate(x, y); final Paint p = getRipplePaint(); if (background != null && background.isVisible()) { background.draw(canvas, p); } if (count > 0) { final RippleForeground[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { ripples[i].draw(canvas, p); } } if (active != null) { active.draw(canvas, p); } canvas.translate(-x, -y); } private void drawMask(Canvas canvas) { mMask.draw(canvas); } Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); } final float x = mHotspotBounds.exactCenterX(); final float y = mHotspotBounds.exactCenterY(); updateMaskShaderIfNeeded(); // Position the shader to account for canvas translation. Loading @@ -871,7 +875,7 @@ public class RippleDrawable extends LayerDrawable { // half so that the ripple and background together yield full alpha. final int color = mState.mColor.getColorForState(getState(), Color.BLACK); final int halfAlpha = (Color.alpha(color) / 2) << 24; final Paint p = getRipplePaint(); final Paint p = mRipplePaint; if (mMaskColorFilter != null) { // The ripple timing depends on the paint's alpha value, so we need Loading @@ -890,35 +894,7 @@ public class RippleDrawable extends LayerDrawable { p.setShader(null); } if (background != null && background.isVisible()) { background.draw(canvas, p); } if (count > 0) { final RippleForeground[] ripples = mExitingRipples; for (int i = 0; i < count; i++) { ripples[i].draw(canvas, p); } } if (active != null) { active.draw(canvas, p); } canvas.translate(-x, -y); } private void drawMask(Canvas canvas) { mMask.draw(canvas); } private Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); mRipplePaint.setAntiAlias(true); mRipplePaint.setStyle(Paint.Style.FILL); } return mRipplePaint; return p; } @Override Loading
graphics/java/android/graphics/drawable/RippleForeground.java +218 −126 File changed.Preview size limit exceeded, changes collapsed. Show changes