Merge "Add GestureTrailsPreview class"
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
new file mode 100644
index 0000000..85558f1
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.os.Message;
+import android.util.SparseArray;
+import android.view.View;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.Params;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+/**
+ * Draw gesture trail preview graphics during gesture.
+ */
+public final class GestureTrailsPreview extends AbstractDrawingPreview {
+    private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
+            CollectionUtils.newSparseArray();
+    private final Params mGesturePreviewTrailParams;
+    private final Paint mGesturePaint;
+    private int mOffscreenWidth;
+    private int mOffscreenHeight;
+    private int mOffscreenOffsetY;
+    private Bitmap mOffscreenBuffer;
+    private final Canvas mOffscreenCanvas = new Canvas();
+    private final Rect mOffscreenSrcRect = new Rect();
+    private final Rect mDirtyRect = new Rect();
+    private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail
+
+    private final DrawingHandler mDrawingHandler;
+
+    private static final class DrawingHandler
+            extends StaticInnerHandlerWrapper<GestureTrailsPreview> {
+        private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 0;
+
+        private final Params mGesturePreviewTrailParams;
+
+        public DrawingHandler(final GestureTrailsPreview outerInstance,
+                final Params gesturePreviewTrailParams) {
+            super(outerInstance);
+            mGesturePreviewTrailParams = gesturePreviewTrailParams;
+        }
+
+        @Override
+        public void handleMessage(final Message msg) {
+            final GestureTrailsPreview preview = getOuterInstance();
+            if (preview == null) return;
+            switch (msg.what) {
+            case MSG_UPDATE_GESTURE_PREVIEW_TRAIL:
+                preview.getDrawingView().invalidate();
+                break;
+            }
+        }
+
+        public void postUpdateGestureTrailPreview() {
+            removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
+            sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
+                    mGesturePreviewTrailParams.mUpdateInterval);
+        }
+    }
+
+    public GestureTrailsPreview(final View drawingView, final TypedArray mainKeyboardViewAttr) {
+        super(drawingView);
+        mGesturePreviewTrailParams = new Params(mainKeyboardViewAttr);
+        mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams);
+        final Paint gesturePaint = new Paint();
+        gesturePaint.setAntiAlias(true);
+        gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+        mGesturePaint = gesturePaint;
+    }
+
+    @Override
+    public void setKeyboardGeometry(final int[] originCoords, final int width, final int height) {
+        mOffscreenOffsetY = (int)(
+                height * GestureStroke.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
+        mOffscreenWidth = width;
+        mOffscreenHeight = mOffscreenOffsetY + height;
+    }
+
+    @Override
+    public void onDetachFromWindow() {
+        freeOffscreenBuffer();
+    }
+
+    private void freeOffscreenBuffer() {
+        if (mOffscreenBuffer != null) {
+            mOffscreenBuffer.recycle();
+            mOffscreenBuffer = null;
+        }
+    }
+
+    private void mayAllocateOffscreenBuffer() {
+        if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
+                && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
+            return;
+        }
+        freeOffscreenBuffer();
+        mOffscreenBuffer = Bitmap.createBitmap(
+                mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
+        mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+        mOffscreenCanvas.translate(0, mOffscreenOffsetY);
+    }
+
+    private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
+            final Rect dirtyRect) {
+        // Clear previous dirty rectangle.
+        if (!dirtyRect.isEmpty()) {
+            paint.setColor(Color.TRANSPARENT);
+            paint.setStyle(Paint.Style.FILL);
+            offscreenCanvas.drawRect(dirtyRect, paint);
+        }
+        dirtyRect.setEmpty();
+        boolean needsUpdatingGesturePreviewTrail = false;
+        // Draw gesture trails to offscreen buffer.
+        synchronized (mGesturePreviewTrails) {
+            // Trails count == fingers count that have ever been active.
+            final int trailsCount = mGesturePreviewTrails.size();
+            for (int index = 0; index < trailsCount; index++) {
+                final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
+                needsUpdatingGesturePreviewTrail |=
+                        trail.drawGestureTrail(offscreenCanvas, paint,
+                                mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
+                // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
+                dirtyRect.union(mGesturePreviewTrailBoundsRect);
+            }
+        }
+        return needsUpdatingGesturePreviewTrail;
+    }
+
+    /**
+     * Draws the preview
+     * @param canvas The canvas where the preview is drawn.
+     */
+    @Override
+    public void drawPreview(final Canvas canvas) {
+        if (!isPreviewEnabled()) {
+            return;
+        }
+        mayAllocateOffscreenBuffer();
+        // Draw gesture trails to offscreen buffer.
+        final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails(
+                mOffscreenCanvas, mGesturePaint, mDirtyRect);
+        if (needsUpdatingGesturePreviewTrail) {
+            mDrawingHandler.postUpdateGestureTrailPreview();
+        }
+        // Transfer offscreen buffer to screen.
+        if (!mDirtyRect.isEmpty()) {
+            mOffscreenSrcRect.set(mDirtyRect);
+            mOffscreenSrcRect.offset(0, mOffscreenOffsetY);
+            canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null);
+            // Note: Defer clearing the dirty rectangle here because we will get cleared
+            // rectangle on the canvas.
+        }
+    }
+
+    /**
+     * Set the position of the preview.
+     * @param tracker The new location of the preview is based on the points in PointerTracker.
+     */
+    @Override
+    public void setPreviewPosition(final PointerTracker tracker) {
+        if (!isPreviewEnabled()) {
+            return;
+        }
+        GesturePreviewTrail trail;
+        synchronized (mGesturePreviewTrails) {
+            trail = mGesturePreviewTrails.get(tracker.mPointerId);
+            if (trail == null) {
+                trail = new GesturePreviewTrail();
+                mGesturePreviewTrails.put(tracker.mPointerId, trail);
+            }
+        }
+        trail.addStroke(tracker.getGestureStrokeWithPreviewPoints(), tracker.getDownTime());
+
+        // TODO: Should narrow the invalidate region.
+        getDrawingView().invalidate();
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index af8bb78..10ef784 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -18,79 +18,26 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.os.Message;
 import android.util.AttributeSet;
-import android.util.SparseArray;
 import android.widget.RelativeLayout;
 
 import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.Params;
-import com.android.inputmethod.latin.CollectionUtils;
 import com.android.inputmethod.latin.CoordinateUtils;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.SuggestedWords;
 
 public final class PreviewPlacerView extends RelativeLayout {
     private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance();
 
-    // TODO: Separate gesture preview trail drawing code into separate class.
-    private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
-            CollectionUtils.newSparseArray();
-    private final Params mGesturePreviewTrailParams;
-    private final Paint mGesturePaint;
-    private boolean mDrawsGesturePreviewTrail;
-    private int mOffscreenWidth;
-    private int mOffscreenHeight;
-    private int mOffscreenOffsetY;
-    private Bitmap mOffscreenBuffer;
-    private final Canvas mOffscreenCanvas = new Canvas();
-    private final Rect mOffscreenSrcRect = new Rect();
-    private final Rect mDirtyRect = new Rect();
-    private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail
     // TODO: Move these AbstractDrawingPvreiew objects to MainKeyboardView.
     private final GestureFloatingPreviewText mGestureFloatingPreviewText;
+    private final GestureTrailsPreview mGestureTrailsPreview;
     private final SlidingKeyInputPreview mSlidingKeyInputPreview;
 
-    private final DrawingHandler mDrawingHandler;
-
-    // TODO: Remove drawing handler.
-    private static final class DrawingHandler extends StaticInnerHandlerWrapper<PreviewPlacerView> {
-        private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 0;
-
-        private final Params mGesturePreviewTrailParams;
-
-        public DrawingHandler(final PreviewPlacerView outerInstance,
-                final Params gesturePreviewTrailParams) {
-            super(outerInstance);
-            mGesturePreviewTrailParams = gesturePreviewTrailParams;
-        }
-
-        @Override
-        public void handleMessage(final Message msg) {
-            final PreviewPlacerView placerView = getOuterInstance();
-            if (placerView == null) return;
-            switch (msg.what) {
-            case MSG_UPDATE_GESTURE_PREVIEW_TRAIL:
-                placerView.invalidate();
-                break;
-            }
-        }
-
-        public void postUpdateGestureTrailPreview() {
-            removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
-            sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
-                    mGesturePreviewTrailParams.mUpdateInterval);
-        }
-    }
-
     public PreviewPlacerView(final Context context, final AttributeSet attrs) {
         this(context, attrs, R.attr.keyboardViewStyle);
     }
@@ -104,17 +51,10 @@
         // TODO: mGestureFloatingPreviewText could be an instance of GestureFloatingPreviewText or
         // MultiGesturePreviewText, depending on the user's choice in the settings.
         mGestureFloatingPreviewText = new GestureFloatingPreviewText(this, mainKeyboardViewAttr);
-        mGesturePreviewTrailParams = new Params(mainKeyboardViewAttr);
+        mGestureTrailsPreview = new GestureTrailsPreview(this, mainKeyboardViewAttr);
         mSlidingKeyInputPreview = new SlidingKeyInputPreview(this, mainKeyboardViewAttr);
         mainKeyboardViewAttr.recycle();
 
-        mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams);
-
-        final Paint gesturePaint = new Paint();
-        gesturePaint.setAntiAlias(true);
-        gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-        mGesturePaint = gesturePaint;
-
         final Paint layerPaint = new Paint();
         layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
         setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
@@ -124,36 +64,21 @@
             final int height) {
         CoordinateUtils.copy(mKeyboardViewOrigin, originCoords);
         mGestureFloatingPreviewText.setKeyboardGeometry(originCoords, width, height);
+        mGestureTrailsPreview.setKeyboardGeometry(originCoords, width, height);
         mSlidingKeyInputPreview.setKeyboardGeometry(originCoords, width, height);
-        mOffscreenOffsetY = (int)(
-                height * GestureStroke.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
-        mOffscreenWidth = width;
-        mOffscreenHeight = mOffscreenOffsetY + height;
     }
 
+    // TODO: Move this method to MainKeyboardView
     public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
             final boolean drawsGestureFloatingPreviewText) {
-        mDrawsGesturePreviewTrail = drawsGesturePreviewTrail;
         mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText);
+        mGestureTrailsPreview.setPreviewEnabled(drawsGesturePreviewTrail);
     }
 
+    // TODO: Move this method to MainKeyboardView
     public void invalidatePointer(final PointerTracker tracker) {
         mGestureFloatingPreviewText.setPreviewPosition(tracker);
-
-        if (mDrawsGesturePreviewTrail) {
-            GesturePreviewTrail trail;
-            synchronized (mGesturePreviewTrails) {
-                trail = mGesturePreviewTrails.get(tracker.mPointerId);
-                if (trail == null) {
-                    trail = new GesturePreviewTrail();
-                    mGesturePreviewTrails.put(tracker.mPointerId, trail);
-                }
-            }
-            trail.addStroke(tracker.getGestureStrokeWithPreviewPoints(), tracker.getDownTime());
-
-            // TODO: Should narrow the invalidate region.
-            invalidate();
-        }
+        mGestureTrailsPreview.setPreviewPosition(tracker);
     }
 
     // TODO: Move this method to MainKeyboardView
@@ -170,27 +95,8 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mGestureFloatingPreviewText.onDetachFromWindow();
+        mGestureTrailsPreview.onDetachFromWindow();
         mSlidingKeyInputPreview.onDetachFromWindow();
-        freeOffscreenBuffer();
-    }
-
-    private void freeOffscreenBuffer() {
-        if (mOffscreenBuffer != null) {
-            mOffscreenBuffer.recycle();
-            mOffscreenBuffer = null;
-        }
-    }
-
-    private void mayAllocateOffscreenBuffer() {
-        if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
-                && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
-            return;
-        }
-        freeOffscreenBuffer();
-        mOffscreenBuffer = Bitmap.createBitmap(
-                mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
-        mOffscreenCanvas.setBitmap(mOffscreenBuffer);
-        mOffscreenCanvas.translate(0, mOffscreenOffsetY);
     }
 
     @Override
@@ -199,54 +105,12 @@
         final int originX = CoordinateUtils.x(mKeyboardViewOrigin);
         final int originY = CoordinateUtils.y(mKeyboardViewOrigin);
         canvas.translate(originX, originY);
-        if (mDrawsGesturePreviewTrail) {
-            mayAllocateOffscreenBuffer();
-            // Draw gesture trails to offscreen buffer.
-            final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails(
-                    mOffscreenCanvas, mGesturePaint, mDirtyRect);
-            if (needsUpdatingGesturePreviewTrail) {
-                mDrawingHandler.postUpdateGestureTrailPreview();
-            }
-            // Transfer offscreen buffer to screen.
-            if (!mDirtyRect.isEmpty()) {
-                mOffscreenSrcRect.set(mDirtyRect);
-                mOffscreenSrcRect.offset(0, mOffscreenOffsetY);
-                canvas.drawBitmap(mOffscreenBuffer, mOffscreenSrcRect, mDirtyRect, null);
-                // Note: Defer clearing the dirty rectangle here because we will get cleared
-                // rectangle on the canvas.
-            }
-        }
         mGestureFloatingPreviewText.drawPreview(canvas);
+        mGestureTrailsPreview.drawPreview(canvas);
         mSlidingKeyInputPreview.drawPreview(canvas);
         canvas.translate(-originX, -originY);
     }
 
-    private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
-            final Rect dirtyRect) {
-        // Clear previous dirty rectangle.
-        if (!dirtyRect.isEmpty()) {
-            paint.setColor(Color.TRANSPARENT);
-            paint.setStyle(Paint.Style.FILL);
-            offscreenCanvas.drawRect(dirtyRect, paint);
-        }
-        dirtyRect.setEmpty();
-        boolean needsUpdatingGesturePreviewTrail = false;
-        // Draw gesture trails to offscreen buffer.
-        synchronized (mGesturePreviewTrails) {
-            // Trails count == fingers count that have ever been active.
-            final int trailsCount = mGesturePreviewTrails.size();
-            for (int index = 0; index < trailsCount; index++) {
-                final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
-                needsUpdatingGesturePreviewTrail |=
-                        trail.drawGestureTrail(offscreenCanvas, paint,
-                                mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
-                // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
-                dirtyRect.union(mGesturePreviewTrailBoundsRect);
-            }
-        }
-        return needsUpdatingGesturePreviewTrail;
-    }
-
     // TODO: Move this method to MainKeyboardView.
     public void setGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
         mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);