Merge "Fix disappearing ripple background, treat active ripple separately" into lmp-dev
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 063ac09..cd919a6 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -105,6 +105,9 @@
/** Whether we have an explicit maximum radius. */
private boolean mHasMaxRadius;
+ /** Whether we were canceled externally and should avoid self-removal. */
+ private boolean mCanceled;
+
/**
* Creates a new ripple.
*/
@@ -295,6 +298,8 @@
* Starts the enter animation.
*/
public void enter() {
+ cancel();
+
final int radiusDuration = (int)
(1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
@@ -332,7 +337,8 @@
* Starts the exit animation.
*/
public void exit() {
- cancelSoftwareAnimations();
+ cancel();
+
final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
final float remaining;
if (mAnimRadius != null && mAnimRadius.isRunning()) {
@@ -399,9 +405,15 @@
invalidateSelf();
}
+ /**
+ * Jump all animations to their end state. The caller is responsible for
+ * removing the ripple from the list of animating ripples.
+ */
public void jump() {
+ mCanceled = true;
endSoftwareAnimations();
endHardwareAnimations();
+ mCanceled = false;
}
private void endSoftwareAnimations() {
@@ -436,6 +448,8 @@
mPendingAnimations.clear();
removeSelf();
}
+
+ mHardwareAnimating = false;
}
private Paint getTempPaint() {
@@ -479,11 +493,14 @@
}
/**
- * Cancel all animations.
+ * Cancels all animations. The caller is responsible for removing
+ * the ripple from the list of animating ripples.
*/
public void cancel() {
+ mCanceled = true;
cancelSoftwareAnimations();
cancelHardwareAnimations(true);
+ mCanceled = false;
}
private void cancelSoftwareAnimations() {
@@ -517,13 +534,16 @@
if (cancelPending && !mPendingAnimations.isEmpty()) {
mPendingAnimations.clear();
- removeSelf();
}
+
+ mHardwareAnimating = false;
}
private void removeSelf() {
// The owner will invalidate itself.
- mOwner.removeRipple(this);
+ if (!mCanceled) {
+ mOwner.removeRipple(this);
+ }
}
private void invalidateSelf() {
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 34e6a20..40a5e18 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -107,6 +107,9 @@
/** Whether we have an explicit maximum radius. */
private boolean mHasMaxRadius;
+ /** Whether we were canceled externally and should avoid self-removal. */
+ private boolean mCanceled;
+
/**
* Creates a new ripple.
*/
@@ -284,6 +287,8 @@
* Starts the enter animation.
*/
public void enter() {
+ cancel();
+
final int radiusDuration = (int)
(1000 * Math.sqrt(mOuterRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
final int outerDuration = (int) (1000 * 1.0f / WAVE_OUTER_OPACITY_ENTER_VELOCITY);
@@ -321,7 +326,7 @@
* Starts the exit animation.
*/
public void exit() {
- cancelSoftwareAnimations();
+ cancel();
// Scale the outer max opacity and opacity velocity based
// on the size of the outer radius.
@@ -404,9 +409,15 @@
invalidateSelf();
}
+ /**
+ * Jump all animations to their end state. The caller is responsible for
+ * removing the ripple from the list of animating ripples.
+ */
public void jump() {
+ mCanceled = true;
endSoftwareAnimations();
endHardwareAnimations();
+ mCanceled = false;
}
private void endSoftwareAnimations() {
@@ -437,6 +448,8 @@
mPendingAnimations.clear();
removeSelf();
}
+
+ mHardwareAnimating = false;
}
private Paint getTempPaint() {
@@ -509,11 +522,14 @@
}
/**
- * Cancel all animations.
+ * Cancel all animations. The caller is responsible for removing
+ * the ripple from the list of animating ripples.
*/
public void cancel() {
+ mCanceled = true;
cancelSoftwareAnimations();
cancelHardwareAnimations(true);
+ mCanceled = false;
}
private void cancelSoftwareAnimations() {
@@ -544,13 +560,16 @@
if (cancelPending && !mPendingAnimations.isEmpty()) {
mPendingAnimations.clear();
- removeSelf();
}
+
+ mHardwareAnimating = false;
}
private void removeSelf() {
// The owner will invalidate itself.
- mOwner.removeBackground(this);
+ if (!mCanceled) {
+ mOwner.removeBackground(this);
+ }
}
private void invalidateSelf() {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 7402bdb..b90fd81 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -140,8 +140,8 @@
* Lazily-created array of actively animating ripples. Inactive ripples are
* pruned during draw(). The locations of these will not change.
*/
- private Ripple[] mAnimatingRipples;
- private int mAnimatingRipplesCount = 0;
+ private Ripple[] mExitingRipples;
+ private int mExitingRipplesCount = 0;
/** Paint used to control appearance of ripples. */
private Paint mRipplePaint;
@@ -156,12 +156,6 @@
private boolean mOverrideBounds;
/**
- * Whether hotspots are being cleared. Used to prevent re-entry by
- * animation finish listeners.
- */
- private boolean mClearingHotspots;
-
- /**
* Constructor used for drawable inflation.
*/
RippleDrawable() {
@@ -209,19 +203,21 @@
mBackground.jump();
}
- mClearingHotspots = true;
- final int count = mAnimatingRipplesCount;
- final Ripple[] ripples = mAnimatingRipples;
+ cancelExitingRipples();
+ invalidateSelf();
+ }
+
+ private void cancelExitingRipples() {
+ final int count = mExitingRipplesCount;
+ final Ripple[] ripples = mExitingRipples;
for (int i = 0; i < count; i++) {
- ripples[i].jump();
+ ripples[i].cancel();
}
+
if (ripples != null) {
Arrays.fill(ripples, 0, count, null);
}
- mAnimatingRipplesCount = 0;
- mClearingHotspots = false;
-
- invalidateSelf();
+ mExitingRipplesCount = 0;
}
@Override
@@ -287,9 +283,9 @@
if (mRippleActive != active) {
mRippleActive = active;
if (active) {
- activateRipple();
+ tryRippleEnter();
} else {
- removeRipple();
+ tryRippleExit();
}
}
}
@@ -298,9 +294,9 @@
if (mBackgroundActive != active) {
mBackgroundActive = active;
if (active) {
- activateBackground();
+ tryBackgroundEnter();
} else {
- removeBackground();
+ tryBackgroundExit();
}
}
}
@@ -327,11 +323,11 @@
// If we just became visible, ensure the background and ripple
// visibilities are consistent with their internal states.
if (mRippleActive) {
- activateRipple();
+ tryRippleEnter();
}
if (mBackgroundActive) {
- activateBackground();
+ tryBackgroundEnter();
}
}
@@ -491,7 +487,7 @@
/**
* Creates an active hotspot at the specified location.
*/
- private void activateBackground() {
+ private void tryBackgroundEnter() {
if (mBackground == null) {
final float x;
final float y;
@@ -511,7 +507,7 @@
mBackground.enter();
}
- private void removeBackground() {
+ private void tryBackgroundExit() {
if (mBackground != null) {
// Don't null out the background, we need it to draw!
mBackground.exit();
@@ -519,10 +515,11 @@
}
/**
- * Creates an active hotspot at the specified location.
+ * Attempts to start an enter animation for the active hotspot. Fails if
+ * there are too many animating ripples.
*/
- private void activateRipple() {
- if (mAnimatingRipplesCount >= MAX_RIPPLES) {
+ private void tryRippleEnter() {
+ if (mExitingRipplesCount >= MAX_RIPPLES) {
// This should never happen unless the user is tapping like a maniac
// or there is a bug that's preventing ripples from being removed.
return;
@@ -545,29 +542,27 @@
final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
mRipple.setup(mState.mMaxRadius, color, mDensity);
mRipple.enter();
-
- if (mAnimatingRipples == null) {
- mAnimatingRipples = new Ripple[MAX_RIPPLES];
- }
- mAnimatingRipples[mAnimatingRipplesCount++] = mRipple;
}
- @Override
- public void invalidateSelf() {
- // Don't invalidate when we're clearing hotspots. We'll handle that
- // manually when we're done.
- if (!mClearingHotspots) {
- super.invalidateSelf();
- }
- }
-
- private void removeRipple() {
+ /**
+ * Attempts to start an exit animation for the active hotspot. Fails if
+ * there is no active hotspot.
+ */
+ private void tryRippleExit() {
if (mRipple != null) {
+ if (mExitingRipples == null) {
+ mExitingRipples = new Ripple[MAX_RIPPLES];
+ }
+ mExitingRipples[mExitingRipplesCount++] = mRipple;
mRipple.exit();
mRipple = null;
}
}
+ /**
+ * Cancels and removes the active ripple, all exiting ripples, and the
+ * background. Nothing will be drawn after this method is called.
+ */
private void clearHotspots() {
if (mRipple != null) {
mRipple.cancel();
@@ -579,18 +574,7 @@
mBackground = null;
}
- mClearingHotspots = true;
- final int count = mAnimatingRipplesCount;
- final Ripple[] ripples = mAnimatingRipples;
- for (int i = 0; i < count; i++) {
- ripples[i].cancel();
- }
- if (ripples != null) {
- Arrays.fill(ripples, 0, count, null);
- }
- mAnimatingRipplesCount = 0;
- mClearingHotspots = false;
-
+ cancelExitingRipples();
invalidateSelf();
}
@@ -612,8 +596,8 @@
* Notifies all the animating ripples that the hotspot bounds have changed.
*/
private void onHotspotBoundsChanged() {
- final int count = mAnimatingRipplesCount;
- final Ripple[] ripples = mAnimatingRipples;
+ final int count = mExitingRipplesCount;
+ final Ripple[] ripples = mExitingRipples;
for (int i = 0; i < count; i++) {
ripples[i].onHotspotBoundsChanged();
}
@@ -683,22 +667,21 @@
}
/**
- * Removes a ripple from the animating ripple list.
+ * Removes a ripple from the exiting ripple list.
*
* @param ripple the ripple to remove
*/
void removeRipple(Ripple ripple) {
- if (!mClearingHotspots) {
- // Ripple ripple ripple ripple. Ripple ripple.
- final Ripple[] ripples = mAnimatingRipples;
- final int count = mAnimatingRipplesCount;
- final int index = getRippleIndex(ripple);
- if (index >= 0) {
- System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1));
- ripples[count - 1] = null;
- mAnimatingRipplesCount--;
- invalidateSelf();
- }
+ // Ripple ripple ripple ripple. Ripple ripple.
+ final Ripple[] ripples = mExitingRipples;
+ final int count = mExitingRipplesCount;
+ final int index = getRippleIndex(ripple);
+ if (index >= 0) {
+ System.arraycopy(ripples, index + 1, ripples, index, count - (index + 1));
+ ripples[count - 1] = null;
+ mExitingRipplesCount--;
+
+ invalidateSelf();
}
}
@@ -710,8 +693,8 @@
}
private int getRippleIndex(Ripple ripple) {
- final Ripple[] ripples = mAnimatingRipples;
- final int count = mAnimatingRipplesCount;
+ final Ripple[] ripples = mExitingRipples;
+ final int count = mExitingRipplesCount;
for (int i = 0; i < count; i++) {
if (ripples[i] == ripple) {
return i;
@@ -727,7 +710,7 @@
// We don't need a layer if we don't expect to draw any ripples, we have
// an explicit mask, or if the non-mask content is all opaque.
boolean needsLayer = false;
- if ((mAnimatingRipplesCount > 0 || mBackground != null) && mMask == null) {
+ if ((mExitingRipplesCount > 0 || mBackground != null) && mMask == null) {
for (int i = 0; i < count; i++) {
if (array[i].mId != R.id.mask
&& array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
@@ -831,10 +814,17 @@
int restoreTranslate = -1;
// Draw ripples and update the animating ripples array.
- final int count = mAnimatingRipplesCount;
- final Ripple[] ripples = mAnimatingRipples;
- for (int i = 0; i < count; i++) {
- final Ripple ripple = ripples[i];
+ final int count = mExitingRipplesCount;
+ final Ripple[] ripples = mExitingRipples;
+ for (int i = 0; i <= count; i++) {
+ final Ripple ripple;
+ if (i < count) {
+ ripple = ripples[i];
+ } else if (mRipple != null) {
+ ripple = mRipple;
+ } else {
+ continue;
+ }
// If we're masking the ripple layer, make sure we have a layer
// first. This will merge SRC_OVER (directly) onto the canvas.
@@ -898,8 +888,9 @@
final int cX = (int) mHotspotBounds.exactCenterX();
final int cY = (int) mHotspotBounds.exactCenterY();
final Rect rippleBounds = mTempRect;
- final Ripple[] activeRipples = mAnimatingRipples;
- final int N = mAnimatingRipplesCount;
+
+ final Ripple[] activeRipples = mExitingRipples;
+ final int N = mExitingRipplesCount;
for (int i = 0; i < N; i++) {
activeRipples[i].getBounds(rippleBounds);
rippleBounds.offset(cX, cY);