diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index d51e036b1..a9856e121 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -826,7 +826,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } final int[] viewOrigin = new int[2]; getLocationInWindow(viewOrigin); - mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]); + mPreviewPlacerView.setKeyboardViewGeometry( + viewOrigin[0], viewOrigin[1], getWidth(), getHeight()); final View rootView = getRootView(); if (rootView == null) { Log.w(TAG, "Cannot find root view"); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index cfd1e09f9..ec8f65994 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -576,18 +576,20 @@ public class PointerTracker implements PointerTrackerQueue.Element { mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker); } - private void updateBatchInput(final long eventTime) { - synchronized (sAggregratedPointers) { - mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers); - final int size = sAggregratedPointers.getPointerSize(); - if (size > sLastRecognitionPointSize - && GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) { - sLastRecognitionPointSize = size; - sLastRecognitionTime = eventTime; - if (DEBUG_LISTENER) { - Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size); + private void mayUpdateBatchInput(final long eventTime, final Key key) { + if (key != null) { + synchronized (sAggregratedPointers) { + mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers); + final int size = sAggregratedPointers.getPointerSize(); + if (size > sLastRecognitionPointSize + && GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) { + sLastRecognitionPointSize = size; + sLastRecognitionTime = eventTime; + if (DEBUG_LISTENER) { + Log.d(TAG, "onUpdateBatchInput: batchPoints=" + size); + } + mListener.onUpdateBatchInput(sAggregratedPointers); } - mListener.onUpdateBatchInput(sAggregratedPointers); } } final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this; @@ -746,8 +748,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (mIsDetectingGesture) { mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); mayStartBatchInput(key); - if (sInGesture && key != null) { - updateBatchInput(eventTime); + if (sInGesture) { + mayUpdateBatchInput(eventTime, key); } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index b3fd284b1..72be7fc59 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -40,6 +40,10 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; public class PreviewPlacerView extends RelativeLayout { + // The height of extra area above the keyboard to draw gesture trails. + // Proportional to the keyboard height. + private static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f; + private final int mGestureFloatingPreviewTextColor; private final int mGestureFloatingPreviewTextOffset; private final int mGestureFloatingPreviewColor; @@ -47,14 +51,17 @@ public class PreviewPlacerView extends RelativeLayout { private final float mGestureFloatingPreviewVerticalPadding; private final float mGestureFloatingPreviewRoundRadius; - private int mXOrigin; - private int mYOrigin; + private int mKeyboardViewOriginX; + private int mKeyboardViewOriginY; private final SparseArray 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 mOffscreenDirtyRect = new Rect(); @@ -165,9 +172,12 @@ public class PreviewPlacerView extends RelativeLayout { setLayerType(LAYER_TYPE_HARDWARE, layerPaint); } - public void setOrigin(final int x, final int y) { - mXOrigin = x; - mYOrigin = y; + public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) { + mKeyboardViewOriginX = x; + mKeyboardViewOriginY = y; + mOffscreenOffsetY = (int)(h * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO); + mOffscreenWidth = w; + mOffscreenHeight = mOffscreenOffsetY + h; } public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail, @@ -204,45 +214,42 @@ public class PreviewPlacerView extends RelativeLayout { @Override protected void onDetachedFromWindow() { + 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); + } + @Override public void onDraw(final Canvas canvas) { super.onDraw(canvas); - canvas.translate(mXOrigin, mYOrigin); if (mDrawsGesturePreviewTrail) { - if (mOffscreenBuffer == null) { - mOffscreenBuffer = Bitmap.createBitmap( - getWidth(), getHeight(), Bitmap.Config.ARGB_8888); - mOffscreenCanvas.setBitmap(mOffscreenBuffer); - } - if (!mOffscreenDirtyRect.isEmpty()) { - // Clear previous dirty rectangle. - mGesturePaint.setColor(Color.TRANSPARENT); - mGesturePaint.setStyle(Paint.Style.FILL); - mOffscreenCanvas.drawRect(mOffscreenDirtyRect, mGesturePaint); - mOffscreenDirtyRect.setEmpty(); - } - boolean needsUpdatingGesturePreviewTrail = false; - 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(mOffscreenCanvas, mGesturePaint, - mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams); - // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail. - mOffscreenDirtyRect.union(mGesturePreviewTrailBoundsRect); - } - } + mayAllocateOffscreenBuffer(); + // Draw gesture trails to offscreen buffer. + final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails( + mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect); + // Transfer offscreen buffer to screen. if (!mOffscreenDirtyRect.isEmpty()) { + final int offsetY = mKeyboardViewOriginY - mOffscreenOffsetY; + canvas.translate(mKeyboardViewOriginX, offsetY); canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect, mGesturePaint); + canvas.translate(-mKeyboardViewOriginX, -offsetY); // Note: Defer clearing the dirty rectangle here because we will get cleared // rectangle on the canvas. } @@ -251,9 +258,49 @@ public class PreviewPlacerView extends RelativeLayout { } } if (mDrawsGestureFloatingPreviewText) { + canvas.translate(mKeyboardViewOriginX, mKeyboardViewOriginY); drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText); + canvas.translate(-mKeyboardViewOriginX, -mKeyboardViewOriginY); } - canvas.translate(-mXOrigin, -mYOrigin); + } + + 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(); + + // Draw gesture trails to offscreen buffer. + offscreenCanvas.translate(0, mOffscreenOffsetY); + boolean needsUpdatingGesturePreviewTrail = false; + 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); + } + } + offscreenCanvas.translate(0, -mOffscreenOffsetY); + + // Clip dirty rectangle with offscreen buffer width/height. + dirtyRect.offset(0, mOffscreenOffsetY); + clipRect(dirtyRect, 0, 0, mOffscreenWidth, mOffscreenHeight); + return needsUpdatingGesturePreviewTrail; + } + + private static void clipRect(final Rect out, final int left, final int top, final int right, + final int bottom) { + out.set(Math.max(out.left, left), Math.max(out.top, top), Math.min(out.right, right), + Math.min(out.bottom, bottom)); } public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {