Bug 718463 - Force redraw when an animation is interrupted by user touch. r=Cwiiis
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 11 Apr 2012 15:46:02 -0500
changeset 91460 66e7ce7034c7d63d92b9f56be337b21b8c340415
parent 91459 7814c8fdc48b5e13074217a53185ad63034baa7f
child 91461 637a5d7228be55ee34dba86a474c9515ac606d36
push id22445
push usereakhgari@mozilla.com
push dateThu, 12 Apr 2012 16:19:55 +0000
treeherdermozilla-central@901dfde60183 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCwiiis
bugs718463
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 718463 - Force redraw when an animation is interrupted by user touch. r=Cwiiis
mobile/android/base/ui/PanZoomController.java
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -262,17 +262,22 @@ public class PanZoomController
     private boolean onTouchStart(MotionEvent event) {
         // user is taking control of movement, so stop
         // any auto-movement we have going
         stopAnimationTimer();
         mSubscroller.cancel();
 
         switch (mState) {
         case ANIMATED_ZOOM:
-            return false;
+            // We just interrupted a double-tap animation, so force a redraw in
+            // case this touchstart is just a tap that doesn't end up triggering
+            // a redraw
+            mController.setForceRedraw();
+            mController.notifyLayerClientOfGeometryChange();
+            // fall through
         case FLING:
         case BOUNCE:
         case NOTHING:
             startTouch(event.getX(0), event.getY(0), event.getEventTime());
             return false;
         case TOUCHING:
         case PANNING:
         case PANNING_LOCKED:
@@ -284,21 +289,25 @@ public class PanZoomController
         }
         Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart");
         return false;
     }
 
     private boolean onTouchMove(MotionEvent event) {
 
         switch (mState) {
-        case NOTHING:
         case FLING:
         case BOUNCE:
             // should never happen
             Log.e(LOGTAG, "Received impossible touch move while in " + mState);
+            // fall through
+        case ANIMATED_ZOOM:
+        case NOTHING:
+            // may happen if user double-taps and drags without lifting after the
+            // second tap. ignore the move if this happens.
             return false;
 
         case TOUCHING:
             if (panDistance(event) < PAN_THRESHOLD) {
                 return false;
             }
             cancelTouch();
             startPanning(event.getX(0), event.getY(0), event.getEventTime());
@@ -318,53 +327,57 @@ public class PanZoomController
         case PANNING_HOLD:
             GeckoApp.mFormAssistPopup.hide();
             mState = PanZoomState.PANNING;
             // fall through
         case PANNING:
             track(event);
             return true;
 
-        case ANIMATED_ZOOM:
         case PINCHING:
             // scale gesture listener will handle this
             return false;
         }
         Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchMove");
         return false;
     }
 
     private boolean onTouchEnd(MotionEvent event) {
 
         switch (mState) {
-        case NOTHING:
         case FLING:
         case BOUNCE:
             // should never happen
             Log.e(LOGTAG, "Received impossible touch end while in " + mState);
+            // fall through
+        case ANIMATED_ZOOM:
+        case NOTHING:
+            // may happen if user double-taps and drags without lifting after the
+            // second tap. ignore if this happens.
             return false;
+
         case TOUCHING:
             mState = PanZoomState.NOTHING;
             // the switch into TOUCHING might have happened while the page was
             // snapping back after overscroll. we need to finish the snap if that
             // was the case
             bounce();
             return false;
+
         case PANNING:
         case PANNING_LOCKED:
         case PANNING_HOLD:
         case PANNING_HOLD_LOCKED:
             mState = PanZoomState.FLING;
             fling();
             return true;
+
         case PINCHING:
             mState = PanZoomState.NOTHING;
             return true;
-        case ANIMATED_ZOOM:
-            return false;
         }
         Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd");
         return false;
     }
 
     private boolean onTouchCancel(MotionEvent event) {
         mState = PanZoomState.NOTHING;
         cancelTouch();
@@ -467,26 +480,27 @@ public class PanZoomController
         stopAnimationTimer();
 
         ViewportMetrics bounceStartMetrics = new ViewportMetrics(mController.getViewportMetrics());
         if (bounceStartMetrics.fuzzyEquals(metrics)) {
             mState = PanZoomState.NOTHING;
             return;
         }
 
-        mState = PanZoomState.BOUNCE;
-        // set the animation target *after* setting state BOUNCE, so that
-        // the getRedrawHint() is returning false and we don't clobber the display
-        // port we set as a result of this animation target call.
+        // At this point we have already set mState to BOUNCE or ANIMATED_ZOOM, so
+        // getRedrawHint() is returning false. This means we can safely call
+        // setAnimationTarget to set the new final display port and not have it get
+        // clobbered by display ports from intermediate animation frames.
         mController.setAnimationTarget(metrics);
         startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
     }
 
     /* Performs a bounce-back animation to the nearest valid viewport metrics. */
     private void bounce() {
+        mState = PanZoomState.BOUNCE;
         bounce(getValidViewportMetrics());
     }
 
     /* Starts the fling or bounce animation. */
     private void startAnimationTimer(final AnimationRunnable runnable) {
         if (mAnimationTimer != null) {
             Log.e(LOGTAG, "Attempted to start a new fling without canceling the old one!");
             stopAnimationTimer();
@@ -590,17 +604,17 @@ public class PanZoomController
         }
 
         protected void animateFrame() {
             /*
              * The pan/zoom controller might have signaled to us that it wants to abort the
              * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail
              * out.
              */
-            if (mState != PanZoomState.BOUNCE) {
+            if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) {
                 finishAnimation();
                 return;
             }
 
             /* Perform the next frame of the bounce-back animation. */
             if (mBounceFrame < EASE_OUT_ANIMATION_FRAMES.length) {
                 advanceBounce();
                 return;