Bug 741693 - Make the zoom animation frames preffable. r=Cwiiis
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 17 Apr 2012 11:33:19 -0400
changeset 91863 1588b18050f408c386d5eb494de28b84d38e1ba7
parent 91862 55f527f49d546342968ff99f5d4e14a15de557e5
child 91864 fb2fd2b621ba207d044c1da377c89569a79e9008
push id22480
push useremorley@mozilla.com
push dateWed, 18 Apr 2012 00:48:48 +0000
treeherdermozilla-central@93dfd98900ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCwiiis
bugs741693
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 741693 - Make the zoom animation frames preffable. r=Cwiiis
mobile/android/app/mobile.js
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/ui/PanZoomController.java
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -742,8 +742,11 @@ pref("ui.scrolling.velocity_threshold", 
 pref("ui.scrolling.max_event_acceleration", -1);
 // The rate of deceleration when the surface has overscrolled, in 1000ths.
 pref("ui.scrolling.overscroll_decel_rate", -1);
 // The fraction of the surface which can be overscrolled before it must snap back, in 1000ths.
 pref("ui.scrolling.overscroll_snap_limit", -1);
 // The minimum amount of space that must be present for an axis to be considered scrollable,
 // in 1/1000ths of pixels.
 pref("ui.scrolling.min_scrollable_distance", -1);
+// A comma-separated list of float values in the range [0.0, 1.0) that are used as
+// interpolation frames for zoom animations.
+pref("ui.zooming.animation_frames", "");
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -256,17 +256,23 @@ public class GeckoLayerClient implements
                 boolean showChecks = message.getBoolean("value");
                 mLayerController.setCheckerboardShowChecks(showChecks);
                 Log.i(LOGTAG, "Showing checks: " + showChecks);
             } else if ("Preferences:Data".equals(event)) {
                 JSONArray jsonPrefs = message.getJSONArray("preferences");
                 Map<String, Integer> prefValues = new HashMap<String, Integer>();
                 for (int i = jsonPrefs.length() - 1; i >= 0; i--) {
                     JSONObject pref = jsonPrefs.getJSONObject(i);
-                    prefValues.put(pref.getString("name"), pref.getInt("value"));
+                    String name = pref.getString("name");
+                    try {
+                        prefValues.put(name, pref.getInt("value"));
+                    } catch (JSONException je) {
+                        // the pref value couldn't be parsed as an int. drop this pref
+                        // and continue with the rest
+                    }
                 }
                 // check return value from setStrategy to make sure that this is the
                 // right batch of prefs, since other java code may also have sent requests
                 // for prefs.
                 if (DisplayPortCalculator.setStrategy(prefValues)) {
                     Axis.setPrefs(prefValues);
                     GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this);
                 }
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -33,33 +33,36 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.ui;
 
+import org.json.JSONArray;
+import org.json.JSONException;
 import org.json.JSONObject;
-import org.json.JSONException;
 import org.mozilla.gecko.gfx.FloatSize;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.PointUtils;
 import org.mozilla.gecko.gfx.ViewportMetrics;
 import org.mozilla.gecko.FloatUtils;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoEventListener;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.FloatMath;
 import android.util.Log;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
+import java.util.Arrays;
+import java.util.StringTokenizer;
 import java.util.Timer;
 import java.util.TimerTask;
 
 /*
  * Handles the kinetic scrolling and zooming physics for a layer controller.
  *
  * Many ideas are from Joe Hewitt's Scrollability:
  *   https://github.com/joehewitt/scrollability/
@@ -67,16 +70,20 @@ import java.util.TimerTask;
 public class PanZoomController
     extends GestureDetector.SimpleOnGestureListener
     implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener
 {
     private static final String LOGTAG = "GeckoPanZoomController";
 
     private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
     private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
+    private static String MESSAGE_PREFS_GET = "Preferences:Get";
+    private static String MESSAGE_PREFS_DATA = "Preferences:Data";
+
+    private static final String PREF_ZOOM_ANIMATION_FRAMES = "ui.zooming.animation_frames";
 
     // Animation stops if the velocity is below this value when overscrolled or panning.
     private static final float STOPPED_THRESHOLD = 4.0f;
 
     // Animation stops is the velocity is below this threshold when flinging.
     private static final float FLING_STOPPED_THRESHOLD = 0.1f;
 
     // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans
@@ -85,17 +92,17 @@ public class PanZoomController
 
     // Angle from axis within which we stay axis-locked
     private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
 
     // The maximum amount we allow you to zoom into a page
     private static final float MAX_ZOOM = 4.0f;
 
     /* 16 precomputed frames of the _ease-out_ animation from the CSS Transitions specification. */
-    private static final float[] EASE_OUT_ANIMATION_FRAMES = {
+    private static float[] ZOOM_ANIMATION_FRAMES = new float[] {
         0.00000f,   /* 0 */
         0.10211f,   /* 1 */
         0.19864f,   /* 2 */
         0.29043f,   /* 3 */
         0.37816f,   /* 4 */
         0.46155f,   /* 5 */
         0.54054f,   /* 6 */
         0.61496f,   /* 7 */
@@ -149,16 +156,21 @@ public class PanZoomController
 
         mMainThread = GeckoApp.mAppContext.getMainLooper().getThread();
         checkMainThread();
 
         mState = PanZoomState.NOTHING;
 
         GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_RECT, this);
         GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_PAGE, this);
+        GeckoAppShell.registerGeckoEventListener(MESSAGE_PREFS_DATA, this);
+
+        JSONArray prefs = new JSONArray();
+        prefs.put(PREF_ZOOM_ANIMATION_FRAMES);
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_PREFS_GET, prefs.toString()));
     }
 
     // for debugging bug 713011; it can be taken out once that is resolved.
     private void checkMainThread() {
         if (mMainThread != Thread.currentThread()) {
             // log with full stack trace
             Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
         }
@@ -190,22 +202,50 @@ public class PanZoomController
                                     y + dh/2,
                                     pageSize.width,
                                     y + dh/2 + newHeight);
                 mController.post(new Runnable() {
                     public void run() {
                         animatedZoomTo(r);
                     }
                 });
+            } else if (MESSAGE_PREFS_DATA.equals(event)) {
+                JSONArray jsonPrefs = message.getJSONArray("preferences");
+                for (int i = jsonPrefs.length() - 1; i >= 0; i--) {
+                    JSONObject pref = jsonPrefs.getJSONObject(i);
+                    String name = pref.getString("name");
+                    if (PREF_ZOOM_ANIMATION_FRAMES.equals(name)) {
+                        setZoomAnimationFrames(pref.getString("value"));
+                        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_PREFS_DATA, this);
+                        break;
+                    }
+                }
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
+    private void setZoomAnimationFrames(String frames) {
+        try {
+            if (frames != null && frames.length() > 0) {
+                StringTokenizer st = new StringTokenizer(frames, ",");
+                float[] values = new float[st.countTokens()];
+                for (int i = 0; i < values.length; i++) {
+                    values[i] = Float.parseFloat(st.nextToken());
+                }
+                ZOOM_ANIMATION_FRAMES = values;
+            }
+        } catch (NumberFormatException e) {
+            Log.e(LOGTAG, "Error setting zoom animation frames", e);
+        } finally {
+            Log.i(LOGTAG, "Zoom animation frames: " + Arrays.toString(ZOOM_ANIMATION_FRAMES));
+        }
+    }
+
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction() & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN:   return onTouchStart(event);
         case MotionEvent.ACTION_MOVE:   return onTouchMove(event);
         case MotionEvent.ACTION_UP:     return onTouchEnd(event);
         case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
         default:                        return false;
         }
@@ -610,31 +650,31 @@ public class PanZoomController
              * out.
              */
             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) {
+            if (mBounceFrame < ZOOM_ANIMATION_FRAMES.length) {
                 advanceBounce();
                 return;
             }
 
             /* Finally, if there's nothing else to do, complete the animation and go to sleep. */
             finishBounce();
             finishAnimation();
             mState = PanZoomState.NOTHING;
         }
 
         /* Performs one frame of a bounce animation. */
         private void advanceBounce() {
             synchronized (mController) {
-                float t = EASE_OUT_ANIMATION_FRAMES[mBounceFrame];
+                float t = ZOOM_ANIMATION_FRAMES[mBounceFrame];
                 ViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
                 mController.setViewportMetrics(newMetrics);
                 mController.notifyLayerClientOfGeometryChange();
                 mBounceFrame++;
             }
         }
 
         /* Concludes a bounce animation and snaps the viewport into place. */