diff options
Diffstat (limited to 'java/src')
6 files changed, 176 insertions, 80 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index cf89567f8..a9856e121 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -781,9 +781,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void cancelAllMessages() { mDrawingHandler.cancelAllMessages(); - if (mPreviewPlacerView != null) { - mPreviewPlacerView.cancelAllMessages(); - } } private TextView getKeyPreviewText(final int pointerId) { @@ -829,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 d6c567ef7..ec8f65994 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -560,10 +560,13 @@ public class PointerTracker implements PointerTrackerQueue.Element { return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size(); } - private void mayStartBatchInput() { + private void mayStartBatchInput(final Key key) { if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) { return; } + if (key == null || !Character.isLetter(key.mCode)) { + return; + } if (DEBUG_LISTENER) { Log.d(TAG, "onStartBatchInput"); } @@ -573,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; @@ -742,9 +747,9 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int gestureTime = (int)(eventTime - sGestureFirstDownTime); if (mIsDetectingGesture) { mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); - mayStartBatchInput(); - if (sInGesture && key != null) { - updateBatchInput(eventTime); + mayStartBatchInput(key); + 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 15170e040..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<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 mOffscreenDirtyRect = new Rect(); @@ -101,29 +108,17 @@ public class PreviewPlacerView extends RelativeLayout { } } - private void cancelDismissGestureFloatingPreviewText() { - removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); - } - public void dismissGestureFloatingPreviewText() { - cancelDismissGestureFloatingPreviewText(); + removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), mGestureFloatingPreviewTextLingerTimeout); } - private void cancelUpdateGestureTrailPreview() { - removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL); - } - public void postUpdateGestureTrailPreview() { - cancelUpdateGestureTrailPreview(); + removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL); sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL), mGesturePreviewTrailParams.mUpdateInterval); } - - public void cancelAllMessages() { - cancelUpdateGestureTrailPreview(); - } } public PreviewPlacerView(final Context context, final AttributeSet attrs) { @@ -177,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, @@ -216,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. } @@ -263,9 +258,49 @@ public class PreviewPlacerView extends RelativeLayout { } } if (mDrawsGestureFloatingPreviewText) { + canvas.translate(mKeyboardViewOriginX, mKeyboardViewOriginY); drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText); + canvas.translate(-mKeyboardViewOriginX, -mKeyboardViewOriginY); + } + } + + 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); + } } - canvas.translate(-mXOrigin, -mYOrigin); + 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) { @@ -278,10 +313,6 @@ public class PreviewPlacerView extends RelativeLayout { mDrawingHandler.dismissGestureFloatingPreviewText(); } - public void cancelAllMessages() { - mDrawingHandler.cancelAllMessages(); - } - private void drawGestureFloatingPreviewText(final Canvas canvas, final String gestureFloatingPreviewText) { if (TextUtils.isEmpty(gestureFloatingPreviewText)) { diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 19da5124a..b97be0543 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -85,7 +85,10 @@ public class BinaryDictIOUtils { } p.mPosition++; - if (info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) { // found word + final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags, + formatOptions); + if (!isMovedGroup + && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word words.put(info.mOriginalAddress, new String(pushedChars, 0, index)); frequencies.put(info.mOriginalAddress, info.mFrequency); if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams); @@ -109,7 +112,7 @@ public class BinaryDictIOUtils { p.mAddress = buffer.position(); } - if (BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) { + if (!isMovedGroup && BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) { Position childrenPos = new Position(info.mChildrenAddress + headerSize, index); stack.push(childrenPos); } @@ -168,6 +171,10 @@ public class BinaryDictIOUtils { final int charGroupPos = buffer.position(); final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, buffer.position(), header.mFormatOptions); + if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags, + header.mFormatOptions)) { + continue; + } boolean same = true; for (int p = 0, j = word.offsetByCodePoints(0, wordPos); p < currentInfo.mCharacters.length; @@ -239,4 +246,34 @@ public class BinaryDictIOUtils { buffer.position(wordPosition); buffer.put((byte)newFlags); } + + private static void putSInt24(final FusionDictionaryBufferInterface buffer, + final int value) { + final int absValue = Math.abs(value); + buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF)); + buffer.put((byte)((absValue >> 8) & 0xFF)); + buffer.put((byte)(absValue & 0xFF)); + } + + /** + * Update a parent address in a CharGroup that is addressed by groupOriginAddress. + * + * @param buffer the buffer to write. + * @param groupOriginAddress the address of the group. + * @param newParentAddress the absolute address of the parent. + * @param formatOptions file format options. + */ + public static void updateParentAddress(final FusionDictionaryBufferInterface buffer, + final int groupOriginAddress, final int newParentAddress, + final FormatOptions formatOptions) { + final int originalPosition = buffer.position(); + buffer.position(groupOriginAddress); + if (!formatOptions.mSupportsDynamicUpdate) { + throw new RuntimeException("this file format does not support parent addresses"); + } + final int flags = buffer.readUnsignedByte(); + final int parentOffset = newParentAddress - groupOriginAddress; + putSInt24(buffer, parentOffset); + buffer.position(originalPosition); + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index f9339de08..9fc694218 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -53,6 +53,7 @@ public class BinaryDictInputOutput { // If the number of passes exceeds this number, makedict bails with an exception on // suspicion that a bug might be causing an infinite loop. private static final int MAX_PASSES = 24; + private static final int MAX_JUMPS = 12; public interface FusionDictionaryBufferInterface { public int readUnsignedByte(); @@ -395,6 +396,13 @@ public class BinaryDictInputOutput { } /** + * Helper method to check whether the group is moved. + */ + public static boolean isMovedGroup(final int flags, final FormatOptions options) { + return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1); + } + + /** * Helper method to check whether the dictionary can be updated dynamically. */ public static boolean supportsDynamicUpdate(final FormatOptions options) { @@ -1374,8 +1382,18 @@ public class BinaryDictInputOutput { int index = FormatSpec.MAX_WORD_LENGTH - 1; // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) { - buffer.position(currentAddress + headerSize); - final CharGroupInfo currentInfo = readCharGroup(buffer, currentAddress, options); + CharGroupInfo currentInfo; + int loopCounter = 0; + do { + buffer.position(currentAddress + headerSize); + currentInfo = readCharGroup(buffer, currentAddress, options); + if (isMovedGroup(currentInfo.mFlags, options)) { + currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress; + } + if (DBG && loopCounter++ > MAX_JUMPS) { + MakedictLog.d("Too many jumps - probably a bug"); + } + } while (isMovedGroup(currentInfo.mFlags, options)); for (int i = 0; i < currentInfo.mCharacters.length; ++i) { sGetWordBuffer[index--] = currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1]; @@ -1457,6 +1475,7 @@ public class BinaryDictInputOutput { int groupOffset = nodeHeadPosition + getGroupCountSize(count); for (int i = count; i > 0; --i) { // Scan the array of CharGroup. CharGroupInfo info = readCharGroup(buffer, groupOffset, options); + if (isMovedGroup(info.mFlags, options)) continue; ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets; ArrayList<WeightedString> bigrams = null; if (null != info.mBigrams) { diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index cab0661f6..35311f0c2 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -52,13 +52,18 @@ public final class FormatSpec { */ /* Node(CharGroup) layout is as follows: - * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE - * 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS - * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE - * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES - * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES - * g | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS - * s | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL + * | IF !SUPPORTS_DYNAMIC_UPDATE + * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE + * | 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS + * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE + * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES + * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES + * g | ELSE + * s | is moved ? 2 bits, 11 = no + * | 01 = yes + * | the new address is stored in the same place as the parent address + * | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS + * | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS * | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD @@ -178,6 +183,7 @@ public final class FormatSpec { static final int FLAG_HAS_BIGRAMS = 0x04; static final int FLAG_IS_NOT_A_WORD = 0x02; static final int FLAG_IS_BLACKLISTED = 0x01; + static final int FLAG_IS_MOVED = 0x40; static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80; static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40; |