Move gesture preview trail parameters to resource

Bug: 7167303
Change-Id: I22b56588dc5a1c9d5eaec3a39b5f32c9e5a8a5b4
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a71e7cc..8a67336 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -108,6 +108,14 @@
         <attr name="backgroundDimAlpha" format="integer" />
         <!-- More keys keyboard will shown at touched point. -->
         <attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" />
+        <!-- Minimum distance between gesture preview trail sampling points. -->
+        <attr name="gesturePreviewTrailMinSamplingDistance" format="dimension" />
+        <!-- Maximum angular threshold between gesture preview trail interpolation segments in degree. -->
+        <attr name="gesturePreviewTrailMaxInterpolationAngularThreshold" format="integer" />
+        <!-- Maximum distance threshold between gesture preview trail interpolation segments. -->
+        <attr name="gesturePreviewTrailMaxInterpolationDistanceThreshold" format="dimension" />
+        <!-- Maximum number of gesture preview trail interpolation segments. -->
+        <attr name="gesturePreviewTrailMaxInterpolationSegments" format="integer" />
         <!-- Delay after gesture trail starts fading out in millisecond. -->
         <attr name="gesturePreviewTrailFadeoutStartDelay" format="integer" />
         <!-- Duration while gesture preview trail is fading out in millisecond. -->
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index da735cf..5c33275 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -101,6 +101,14 @@
     <fraction name="center_suggestion_percentile">36%</fraction>
 
     <!-- Gesture preview trail parameters -->
+    <!-- Minimum distance between gesture preview trail sampling points. -->
+    <dimen name="gesture_preview_trail_min_sampling_distance">6.4dp</dimen>
+    <!-- Maximum angular threshold between gesture preview trails interpolation segments in degree. -->
+    <integer name="gesture_preview_trail_max_interpolation_angular_threshold">15</integer>
+    <!-- Maximum distance threshold between gesture preview trails interpolation segments. -->
+    <dimen name="gesture_preview_trail_max_interpolation_distance_threshold">16.0dp</dimen>
+    <!-- Maximum number of gesture preview trail interpolation segments. -->
+    <integer name="gesture_preview_trail_max_interpolation_segments">6</integer>
     <dimen name="gesture_preview_trail_start_width">10.0dp</dimen>
     <dimen name="gesture_preview_trail_end_width">2.5dp</dimen>
     <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width.
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index dad7e20..fa40e51 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -64,6 +64,10 @@
         <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item>
         <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item>
         <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item>
+        <item name="gesturePreviewTrailMinSamplingDistance">@dimen/gesture_preview_trail_min_sampling_distance</item>
+        <item name="gesturePreviewTrailMaxInterpolationAngularThreshold">@integer/gesture_preview_trail_max_interpolation_angular_threshold</item>
+        <item name="gesturePreviewTrailMaxInterpolationDistanceThreshold">@dimen/gesture_preview_trail_max_interpolation_distance_threshold</item>
+        <item name="gesturePreviewTrailMaxInterpolationSegments">@integer/gesture_preview_trail_max_interpolation_segments</item>
         <item name="gesturePreviewTrailFadeoutStartDelay">@integer/config_gesture_preview_trail_fadeout_start_delay</item>
         <item name="gesturePreviewTrailFadeoutDuration">@integer/config_gesture_preview_trail_fadeout_duration</item>
         <item name="gesturePreviewTrailUpdateInterval">@integer/config_gesture_preview_trail_update_interval</item>
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 0556fdd..2d79164 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -25,6 +25,7 @@
 import com.android.inputmethod.keyboard.internal.GestureStroke;
 import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
 import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
+import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints.GestureStrokePreviewParams;
 import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
 import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.Constants;
@@ -161,6 +162,7 @@
     // Parameters for pointer handling.
     private static PointerTrackerParams sParams;
     private static GestureStrokeParams sGestureStrokeParams;
+    private static GestureStrokePreviewParams sGesturePreviewParams;
     private static boolean sNeedsPhantomSuddenMoveEventHack;
     // Move this threshold to resource.
     // TODO: Device specific parameter would be better for device specific hack?
@@ -339,12 +341,14 @@
         sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
         sParams = PointerTrackerParams.DEFAULT;
         sGestureStrokeParams = GestureStrokeParams.DEFAULT;
+        sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
         sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
     }
 
     public static void setParameters(final TypedArray mainKeyboardViewAttr) {
         sParams = new PointerTrackerParams(mainKeyboardViewAttr);
         sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
+        sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
         sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
     }
 
@@ -428,7 +432,7 @@
         }
         mPointerId = id;
         mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
-                id, sGestureStrokeParams);
+                id, sGestureStrokeParams, sGesturePreviewParams);
         setKeyDetectorInner(handler.getKeyDetector());
         mListener = handler.getKeyboardActionListener();
         mDrawingProxy = handler.getDrawingProxy();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
index 7a51e25..312fd21 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -16,6 +16,9 @@
 
 package com.android.inputmethod.keyboard.internal;
 
+import android.content.res.TypedArray;
+
+import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.ResizableIntArray;
 
 public final class GestureStrokeWithPreviewPoints extends GestureStroke {
@@ -25,6 +28,8 @@
     private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
     private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
 
+    private final GestureStrokePreviewParams mPreviewParams;
+
     private int mStrokeId;
     private int mLastPreviewSize;
     private final HermiteInterpolator mInterpolator = new HermiteInterpolator();
@@ -32,23 +37,53 @@
 
     private int mLastX;
     private int mLastY;
-    private double mMinPreviewSamplingDistance;
     private double mDistanceFromLastSample;
-    private double mInterpolationDistanceThreshold;
 
-    // TODO: Move these constants to resource.
-    // TODO: Use "dp" instead of ratio to the keyWidth because table has rather large keys.
-    // The minimum trail distance between sample points for preview in keyWidth unit when using
-    // interpolation.
-    private static final float MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH = 0.2f;
-    // The angular threshold to use interpolation in radian. PI/12 is 15 degree.
-    private static final double INTERPOLATION_ANGULAR_THRESHOLD = Math.PI / 12.0d;
-    // The distance threshold to use interpolation in keyWidth unit.
-    private static final float INTERPOLATION_DISTANCE_THRESHOLD_TO_KEY_WIDTH = 0.5f;
-    private static final int MAX_INTERPOLATION_PARTITIONS = 6;
+    public static final class GestureStrokePreviewParams {
+        public final double mMinSamplingDistance; // in pixel
+        public final double mMaxInterpolationAngularThreshold; // in radian
+        public final double mMaxInterpolationDistanceThreshold; // in pixel
+        public final int mMaxInterpolationSegments;
 
-    public GestureStrokeWithPreviewPoints(final int pointerId, final GestureStrokeParams params) {
-        super(pointerId, params);
+        public static final GestureStrokePreviewParams DEFAULT = new GestureStrokePreviewParams();
+
+        private static final int DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD = 15; // in degree
+
+        private GestureStrokePreviewParams() {
+            mMinSamplingDistance = 0.0d;
+            mMaxInterpolationAngularThreshold =
+                    degreeToRadian(DEFAULT_MAX_INTERPOLATION_ANGULAR_THRESHOLD);
+            mMaxInterpolationDistanceThreshold = mMinSamplingDistance;
+            mMaxInterpolationSegments = 4;
+        }
+
+        private static double degreeToRadian(final int degree) {
+            return (double)degree / 180.0d * Math.PI;
+        }
+
+        public GestureStrokePreviewParams(final TypedArray mainKeyboardViewAttr) {
+            mMinSamplingDistance = mainKeyboardViewAttr.getDimension(
+                    R.styleable.MainKeyboardView_gesturePreviewTrailMinSamplingDistance,
+                    (float)DEFAULT.mMinSamplingDistance);
+            final int interpolationAngularDegree = mainKeyboardViewAttr.getInteger(R.styleable
+                    .MainKeyboardView_gesturePreviewTrailMaxInterpolationAngularThreshold, 0);
+            mMaxInterpolationAngularThreshold = (interpolationAngularDegree <= 0)
+                    ? DEFAULT.mMaxInterpolationAngularThreshold
+                    : degreeToRadian(interpolationAngularDegree);
+            mMaxInterpolationDistanceThreshold = mainKeyboardViewAttr.getDimension(R.styleable
+                    .MainKeyboardView_gesturePreviewTrailMaxInterpolationDistanceThreshold,
+                    (float)DEFAULT.mMaxInterpolationDistanceThreshold);
+            mMaxInterpolationSegments = mainKeyboardViewAttr.getInteger(
+                    R.styleable.MainKeyboardView_gesturePreviewTrailMaxInterpolationSegments,
+                    DEFAULT.mMaxInterpolationSegments);
+        }
+    }
+
+    public GestureStrokeWithPreviewPoints(final int pointerId,
+            final GestureStrokeParams strokeParams,
+            final GestureStrokePreviewParams previewParams) {
+        super(pointerId, strokeParams);
+        mPreviewParams = previewParams;
     }
 
     @Override
@@ -66,19 +101,12 @@
         return mStrokeId;
     }
 
-    @Override
-    public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
-        super.setKeyboardGeometry(keyWidth, keyboardHeight);
-        mMinPreviewSamplingDistance = keyWidth * MIN_PREVIEW_SAMPLING_RATIO_TO_KEY_WIDTH;
-        mInterpolationDistanceThreshold = keyWidth * INTERPOLATION_DISTANCE_THRESHOLD_TO_KEY_WIDTH;
-    }
-
     private boolean needsSampling(final int x, final int y) {
         mDistanceFromLastSample += Math.hypot(x - mLastX, y - mLastY);
         mLastX = x;
         mLastY = y;
         final boolean isDownEvent = (mPreviewEventTimes.getLength() == 0);
-        if (mDistanceFromLastSample >= mMinPreviewSamplingDistance || isDownEvent) {
+        if (mDistanceFromLastSample >= mPreviewParams.mMinSamplingDistance || isDownEvent) {
             mDistanceFromLastSample = 0.0d;
             return true;
         }
@@ -144,19 +172,19 @@
             final double m1 = Math.atan2(mInterpolator.mSlope1Y, mInterpolator.mSlope1X);
             final double m2 = Math.atan2(mInterpolator.mSlope2Y, mInterpolator.mSlope2X);
             final double deltaAngle = Math.abs(angularDiff(m2, m1));
-            final int partitionsByAngle = (int)Math.ceil(
-                    deltaAngle / INTERPOLATION_ANGULAR_THRESHOLD);
+            final int segmentsByAngle = (int)Math.ceil(
+                    deltaAngle / mPreviewParams.mMaxInterpolationAngularThreshold);
             final double deltaDistance = Math.hypot(mInterpolator.mP1X - mInterpolator.mP2X,
                     mInterpolator.mP1Y - mInterpolator.mP2Y);
-            final int partitionsByDistance = (int)Math.ceil(deltaDistance
-                    / mInterpolationDistanceThreshold);
-            final int partitions = Math.min(MAX_INTERPOLATION_PARTITIONS,
-                    Math.max(partitionsByAngle, partitionsByDistance));
+            final int segmentsByDistance = (int)Math.ceil(deltaDistance
+                    / mPreviewParams.mMaxInterpolationDistanceThreshold);
+            final int segments = Math.min(mPreviewParams.mMaxInterpolationSegments,
+                    Math.max(segmentsByAngle, segmentsByDistance));
             final int t1 = eventTimes.get(d1);
             final int dt = pt[p2] - pt[p1];
             d1++;
-            for (int i = 1; i < partitions; i++) {
-                final float t = i / (float)partitions;
+            for (int i = 1; i < segments; i++) {
+                final float t = i / (float)segments;
                 mInterpolator.interpolate(t);
                 eventTimes.add(d1, (int)(dt * t) + t1);
                 xCoords.add(d1, (int)mInterpolator.mInterpolatedX);