diff options
22 files changed, 724 insertions, 291 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index bb1aa4776..8880af48c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -273,7 +273,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { public void startDoubleTapShiftKeyTimer() { final MainKeyboardView keyboardView = getMainKeyboardView(); if (keyboardView != null) { - keyboardView.getTimerProxy().startDoubleTapShiftKeyTimer(); + keyboardView.startDoubleTapShiftKeyTimer(); } } @@ -282,7 +282,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { public void cancelDoubleTapShiftKeyTimer() { final MainKeyboardView keyboardView = getMainKeyboardView(); if (keyboardView != null) { - keyboardView.getTimerProxy().cancelDoubleTapShiftKeyTimer(); + keyboardView.cancelDoubleTapShiftKeyTimer(); } } @@ -290,7 +290,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { @Override public boolean isInDoubleTapShiftKeyTimeout() { final MainKeyboardView keyboardView = getMainKeyboardView(); - return keyboardView != null && keyboardView.getTimerProxy().isInDoubleTapShiftKeyTimeout(); + return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout(); } /** @@ -314,10 +314,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { R.layout.input_view, null); mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); - if (isHardwareAcceleratedDrawingEnabled) { - mKeyboardView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? - } + mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); mKeyboardView.setKeyboardActionListener(mLatinIME); // This always needs to be set since the accessibility state can diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 254b20b87..054c503d8 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -154,6 +154,12 @@ public class KeyboardView extends View { Color.red(color), Color.green(color), Color.blue(color)); } + public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { + if (!enabled) return; + // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? + setLayerType(LAYER_TYPE_HARDWARE, null); + } + /** * Attaches a keyboard to this view. The keyboard can be switched at any time and the * view will re-layout itself to accommodate the keyboard. @@ -605,4 +611,8 @@ public class KeyboardView extends View { super.onDetachedFromWindow(); freeOffscreenBuffer(); } + + public void deallocateMemory() { + freeOffscreenBuffer(); + } } diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 43baf6172..6782317d0 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -53,6 +53,7 @@ import com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText; import com.android.inputmethod.keyboard.internal.GestureTrailsPreview; import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; +import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview; import com.android.inputmethod.latin.Constants; @@ -179,9 +180,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private int mGestureFloatingPreviewTextLingerTimeout; private KeyDetector mKeyDetector; - private final boolean mHasDistinctMultitouch; - private int mOldPointerCount = 1; - private Key mOldKey; + private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; private final KeyTimerHandler mKeyTimerHandler; @@ -193,8 +192,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3; private static final int MSG_UPDATE_BATCH_INPUT = 4; - private final int mKeyRepeatStartTimeout; - private final int mKeyRepeatInterval; private final int mIgnoreAltCodeKeyTimeout; private final int mGestureRecognitionUpdateTime; @@ -202,10 +199,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final TypedArray mainKeyboardViewAttr) { super(outerInstance); - mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0); - mKeyRepeatInterval = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_keyRepeatInterval, 0); mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( @@ -224,17 +217,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack startWhileTypingFadeinAnimation(keyboardView); break; case MSG_REPEAT_KEY: - final Key currentKey = tracker.getKey(); - final int code = msg.arg1; - if (currentKey != null && currentKey.mCode == code) { - startKeyRepeatTimer(tracker, mKeyRepeatInterval); - startTypingStateTimer(currentKey); - final KeyboardActionListener listener = - keyboardView.getKeyboardActionListener(); - listener.onPressKey(code, true /* isRepeatKey */, true /* isSinglePointer */); - listener.onCodeInput(code, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } + tracker.onKeyRepeat(msg.arg1); break; case MSG_LONGPRESS_KEY: keyboardView.onLongPress(tracker); @@ -246,19 +229,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } - private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) { + @Override + public void startKeyRepeatTimer(final PointerTracker tracker, final int delay) { final Key key = tracker.getKey(); - if (key == null) { + if (key == null || delay == 0) { return; } sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay); } - @Override - public void startKeyRepeatTimer(final PointerTracker tracker) { - startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout); - } - public void cancelKeyRepeatTimer() { removeMessages(MSG_REPEAT_KEY); } @@ -443,13 +422,16 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); + PointerTracker.init(getResources()); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final boolean forceNonDistinctMultitouch = prefs.getBoolean( DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false); final boolean hasDistinctMultitouch = context.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); - mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch; - PointerTracker.init(getResources()); + .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT) + && !forceNonDistinctMultitouch; + mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null + : new NonDistinctMultitouchHelper(); + mPreviewPlacerView = new PreviewPlacerView(context, attrs); final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( @@ -530,6 +512,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; } + @Override + public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { + super.setHardwareAcceleratedDrawingEnabled(enabled); + mPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled); + } + private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { if (resId == 0) { // TODO: Stop returning null. @@ -1027,6 +1015,18 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } + public void startDoubleTapShiftKeyTimer() { + mKeyTimerHandler.startDoubleTapShiftKeyTimer(); + } + + public void cancelDoubleTapShiftKeyTimer() { + mKeyTimerHandler.cancelDoubleTapShiftKeyTimer(); + } + + public boolean isInDoubleTapShiftKeyTimeout() { + return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout(); + } + @Override public boolean dispatchTouchEvent(MotionEvent event) { if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { @@ -1040,100 +1040,31 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (getKeyboard() == null) { return false; } - // TODO: Add multi-touch to single-touch event converter for non-distinct multi-touch - // device. + if (mNonDistinctMultitouchHelper != null) { + if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { + // Key repeating timer will be canceled if 2 or more keys are in action. + mKeyTimerHandler.cancelKeyRepeatTimer(); + } + // Non distinct multitouch screen support + mNonDistinctMultitouchHelper.processMotionEvent(me, this); + return true; + } return processMotionEvent(me); } public boolean processMotionEvent(final MotionEvent me) { - final boolean nonDistinctMultitouch = !mHasDistinctMultitouch; - final int action = me.getActionMasked(); - final int pointerCount = me.getPointerCount(); - final int oldPointerCount = mOldPointerCount; - mOldPointerCount = pointerCount; - - // TODO: cleanup this code into a multi-touch to single-touch event converter class? - // If the device does not have distinct multi-touch support panel, ignore all multi-touch - // events except a transition from/to single-touch. - if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { - return true; - } - - final long eventTime = me.getEventTime(); - final int index = me.getActionIndex(); - final int id = me.getPointerId(index); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - - // TODO: This might be moved to the tracker.processMotionEvent() call below. if (LatinImeLogger.sUsabilityStudy) { UsabilityStudyLogUtils.writeMotionEvent(me); } - // TODO: This should be moved to the tracker.processMotionEvent() call below. // Currently the same "move" event is being logged twice. if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.mainKeyboardView_processMotionEvent( - me, action, eventTime, index, id, x, y); - } - - if (mKeyTimerHandler.isInKeyRepeat()) { - final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); - // Key repeating timer will be canceled if 2 or more keys are in action, and current - // event (UP or DOWN) is non-modifier key. - if (pointerCount > 1 && !tracker.isModifier()) { - mKeyTimerHandler.cancelKeyRepeatTimer(); - } - // Up event will pass through. - } - - // TODO: cleanup this code into a multi-touch to single-touch event converter class? - // Translate mutli-touch event to single-touch events on the device that has no distinct - // multi-touch panel. - if (nonDistinctMultitouch) { - // Use only main (id=0) pointer tracker. - final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); - if (pointerCount == 1 && oldPointerCount == 2) { - // Multi-touch to single touch transition. - // Send a down event for the latest pointer if the key is different from the - // previous key. - final Key newKey = tracker.getKeyOn(x, y); - if (mOldKey != newKey) { - tracker.onDownEvent(x, y, eventTime, this); - if (action == MotionEvent.ACTION_UP) { - tracker.onUpEvent(x, y, eventTime); - } - } - } else if (pointerCount == 2 && oldPointerCount == 1) { - // Single-touch to multi-touch transition. - // Send an up event for the last pointer. - final int[] lastCoords = CoordinateUtils.newInstance(); - mOldKey = tracker.getKeyOn( - CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords)); - tracker.onUpEvent( - CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime); - } else if (pointerCount == 1 && oldPointerCount == 1) { - tracker.processMotionEvent(action, x, y, eventTime, this); - } else { - Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount - + " (old " + oldPointerCount + ")"); - } - return true; - } - - if (action == MotionEvent.ACTION_MOVE) { - for (int i = 0; i < pointerCount; i++) { - final int pointerId = me.getPointerId(i); - final PointerTracker tracker = PointerTracker.getPointerTracker( - pointerId, this); - final int px = (int)me.getX(i); - final int py = (int)me.getY(i); - tracker.onMoveEvent(px, py, eventTime, me); - } - } else { - final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); - tracker.processMotionEvent(action, x, y, eventTime, this); + ResearchLogger.mainKeyboardView_processMotionEvent(me); } + final int index = me.getActionIndex(); + final int id = me.getPointerId(index); + final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); + tracker.processMotionEvent(me, this); return true; } @@ -1330,7 +1261,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } + @Override public void deallocateMemory() { + super.deallocateMemory(); mGestureTrailsPreview.deallocateMemory(); } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 20fc109da..ab5fee99d 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -94,7 +94,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public interface TimerProxy { public void startTypingStateTimer(Key typedKey); public boolean isTypingState(); - public void startKeyRepeatTimer(PointerTracker tracker); + public void startKeyRepeatTimer(PointerTracker tracker, int delay); public void startLongPressTimer(PointerTracker tracker, int delay); public void cancelLongPressTimer(); public void startDoubleTapShiftKeyTimer(); @@ -111,7 +111,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { @Override public boolean isTypingState() { return false; } @Override - public void startKeyRepeatTimer(PointerTracker tracker) {} + public void startKeyRepeatTimer(PointerTracker tracker, int delay) {} @Override public void startLongPressTimer(PointerTracker tracker, int delay) {} @Override @@ -138,6 +138,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public final int mTouchNoiseThresholdTime; public final int mTouchNoiseThresholdDistance; public final int mSuppressKeyPreviewAfterBatchInputDuration; + public final int mKeyRepeatStartTimeout; + public final int mKeyRepeatInterval; public final int mLongPressShiftLockTimeout; public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); @@ -147,6 +149,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mTouchNoiseThresholdTime = 0; mTouchNoiseThresholdDistance = 0; mSuppressKeyPreviewAfterBatchInputDuration = 0; + mKeyRepeatStartTimeout = 0; + mKeyRepeatInterval = 0; mLongPressShiftLockTimeout = 0; } @@ -159,6 +163,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0); mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0); + mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0); + mKeyRepeatInterval = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyRepeatInterval, 0); mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0); } @@ -470,6 +478,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mPointerId = id; mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints( id, sGestureStrokeParams, sGesturePreviewParams); + setKeyEventHandler(handler); + } + + private void setKeyEventHandler(final KeyEventHandler handler) { setKeyDetectorInner(handler.getKeyDetector()); mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); @@ -477,7 +489,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } // Returns true if keyboard has been changed by this callback. - private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) { + private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key, + final boolean isRepeatKey) { // While gesture input is going on, this method should be a no-operation. But when gesture // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code> // are set to false. To keep this method is a no-operation, @@ -487,17 +500,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier(); if (DEBUG_LISTENER) { - Log.d(TAG, String.format("[%d] onPress : %s%s%s", mPointerId, + Log.d(TAG, String.format("[%d] onPress : %s%s%s%s", mPointerId, KeyDetector.printableCode(key), ignoreModifierKey ? " ignoreModifier" : "", - key.isEnabled() ? "" : " disabled")); + key.isEnabled() ? "" : " disabled", + isRepeatKey ? " repeat" : "")); } if (ignoreModifierKey) { return false; } if (key.isEnabled()) { - mListener.onPressKey(key.mCode, false /* isRepeatKey */, - getActivePointerTrackerCount() == 1); + mListener.onPressKey(key.mCode, isRepeatKey, getActivePointerTrackerCount() == 1); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; mKeyboardLayoutHasBeenChanged = false; mTimerProxy.startTypingStateTimer(key); @@ -857,8 +870,23 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mListener.onCancelBatchInput(); } - public void processMotionEvent(final int action, final int x, final int y, final long eventTime, - final KeyEventHandler handler) { + public void processMotionEvent(final MotionEvent me, final KeyEventHandler handler) { + final int action = me.getActionMasked(); + final long eventTime = me.getEventTime(); + if (action == MotionEvent.ACTION_MOVE) { + final int pointerCount = me.getPointerCount(); + for (int index = 0; index < pointerCount; index++) { + final int id = me.getPointerId(index); + final PointerTracker tracker = getPointerTracker(id, handler); + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); + tracker.onMoveEvent(x, y, eventTime, me); + } + return; + } + final int index = me.getActionIndex(); + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: @@ -868,24 +896,18 @@ public final class PointerTracker implements PointerTrackerQueue.Element { case MotionEvent.ACTION_POINTER_UP: onUpEvent(x, y, eventTime); break; - case MotionEvent.ACTION_MOVE: - onMoveEvent(x, y, eventTime, null); - break; case MotionEvent.ACTION_CANCEL: onCancelEvent(x, y, eventTime); break; } } - public void onDownEvent(final int x, final int y, final long eventTime, + private void onDownEvent(final int x, final int y, final long eventTime, final KeyEventHandler handler) { if (DEBUG_EVENT) { printTouchEvent("onDownEvent:", x, y, eventTime); } - mDrawingProxy = handler.getDrawingProxy(); - mTimerProxy = handler.getTimerProxy(); - setKeyboardActionListener(handler.getKeyboardActionListener()); - setKeyDetectorInner(handler.getKeyDetector()); + setKeyEventHandler(handler); // Naive up-to-down noise filter. final long deltaT = eventTime - mUpTime; if (deltaT < sParams.mTouchNoiseThresholdTime) { @@ -917,7 +939,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } // A gesture should start only from a non-modifier key. mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard() - && key != null && !key.isModifier(); + && key != null && !key.isModifier() && !key.isRepeatable(); if (mIsDetectingGesture) { if (getActivePointerTrackerCount() == 1) { sGestureFirstDownTime = eventTime; @@ -945,7 +967,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // This onPress call may have changed keyboard layout. Those cases are detected at // {@link #setKeyboard}. In those cases, we should update key according to the new // keyboard layout. - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { + if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) { key = onDownKey(x, y, eventTime); } @@ -995,7 +1017,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) { + private void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) { if (DEBUG_MOVE_EVENT) { printTouchEvent("onMoveEvent:", x, y, eventTime); } @@ -1035,7 +1057,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // at {@link #setKeyboard}. In those cases, we should update key according // to the new keyboard layout. Key key = newKey; - if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) { + if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) { key = onMoveKey(x, y); } onMoveToNewKey(key, x, y); @@ -1183,7 +1205,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - public void onUpEvent(final int x, final int y, final long eventTime) { + private void onUpEvent(final int x, final int y, final long eventTime) { if (DEBUG_EVENT) { printTouchEvent("onUpEvent :", x, y, eventTime); } @@ -1283,7 +1305,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { sPointerTrackerQueue.remove(this); } - public void onCancelEvent(final int x, final int y, final long eventTime) { + private void onCancelEvent(final int x, final int y, final long eventTime) { if (DEBUG_EVENT) { printTouchEvent("onCancelEvt:", x, y, eventTime); } @@ -1304,16 +1326,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - private void startRepeatKey(final Key key) { - if (sInGesture) return; - if (key == null) return; - if (!key.isRepeatable()) return; - // Don't start key repeat when we are in sliding input mode. - if (mIsInSlidingKeyInput) return; - detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis()); - mTimerProxy.startKeyRepeatTimer(this); - } - private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime, final Key newKey) { if (mKeyDetector == null) { @@ -1394,6 +1406,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element { callListenerOnRelease(key, code, false /* withSliding */); } + private void startRepeatKey(final Key key) { + if (sInGesture) return; + if (key == null) return; + if (!key.isRepeatable()) return; + // Don't start key repeat when we are in sliding input mode. + if (mIsInSlidingKeyInput) return; + detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis()); + mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatStartTimeout); + } + + public void onKeyRepeat(final int code) { + final Key key = getKey(); + if (key == null || key.mCode != code) { + return; + } + mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatInterval); + callListenerOnPressAndCheckKeyboardLayoutChange(key, true /* isRepeatKey */); + callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis()); + } + private void printTouchEvent(final String title, final int x, final int y, final long eventTime) { final Key key = mKeyDetector.detectHitKey(x, y); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java index d4c25941c..19e995548 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java @@ -110,6 +110,7 @@ public final class GestureTrailsPreview extends AbstractDrawingPreview { private void freeOffscreenBuffer() { mOffscreenCanvas.setBitmap(null); + mOffscreenCanvas.setMatrix(null); if (mOffscreenBuffer != null) { mOffscreenBuffer.recycle(); mOffscreenBuffer = null; diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java new file mode 100644 index 000000000..a0935b985 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java @@ -0,0 +1,113 @@ +/* + * 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.util.Log; +import android.view.MotionEvent; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; +import com.android.inputmethod.latin.utils.CoordinateUtils; + +public final class NonDistinctMultitouchHelper { + private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName(); + + private int mOldPointerCount = 1; + private Key mOldKey; + private int[] mLastCoords = CoordinateUtils.newInstance(); + + public void processMotionEvent(final MotionEvent me, final KeyEventHandler keyEventHandler) { + final int pointerCount = me.getPointerCount(); + final int oldPointerCount = mOldPointerCount; + mOldPointerCount = pointerCount; + // Ignore continuous multi-touch events because we can't trust the coordinates + // in multi-touch events. + if (pointerCount > 1 && oldPointerCount > 1) { + return; + } + + // Use only main (id=0) pointer tracker. + final PointerTracker mainTracker = PointerTracker.getPointerTracker(0, keyEventHandler); + final int action = me.getActionMasked(); + final int index = me.getActionIndex(); + final long eventTime = me.getEventTime(); + final long downTime = me.getDownTime(); + + // In single-touch. + if (oldPointerCount == 1 && pointerCount == 1) { + if (me.getPointerId(index) == mainTracker.mPointerId) { + mainTracker.processMotionEvent(me, keyEventHandler); + return; + } + // Inject a copied event. + injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime, + mainTracker, keyEventHandler); + return; + } + + // Single-touch to multi-touch transition. + if (oldPointerCount == 1 && pointerCount == 2) { + // Send an up event for the last pointer, be cause we can't trust the coordinates of + // this multi-touch event. + mainTracker.getLastCoordinates(mLastCoords); + final int x = CoordinateUtils.x(mLastCoords); + final int y = CoordinateUtils.y(mLastCoords); + mOldKey = mainTracker.getKeyOn(x, y); + // Inject an artifact up event for the old key. + injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, + mainTracker, keyEventHandler); + return; + } + + // Multi-touch to single-touch transition. + if (oldPointerCount == 2 && pointerCount == 1) { + // Send a down event for the latest pointer if the key is different from the previous + // key. + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); + final Key newKey = mainTracker.getKeyOn(x, y); + if (mOldKey != newKey) { + // Inject an artifact down event for the new key. + // An artifact up event for the new key will usually be injected as a single-touch. + injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime, + mainTracker, keyEventHandler); + if (action == MotionEvent.ACTION_UP) { + // Inject an artifact up event for the new key also. + injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, + mainTracker, keyEventHandler); + } + } + return; + } + + Log.w(TAG, "Unknown touch panel behavior: pointer count is " + + pointerCount + " (previously " + oldPointerCount + ")"); + } + + private static void injectMotionEvent(final int action, final float x, final float y, + final long downTime, final long eventTime, final PointerTracker tracker, + final KeyEventHandler handler) { + final MotionEvent me = MotionEvent.obtain( + downTime, eventTime, action, x, y, 0 /* metaState */); + try { + tracker.processMotionEvent(me, handler); + } finally { + me.recycle(); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 3388c5701..4c8607da8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -37,7 +37,10 @@ public final class PreviewPlacerView extends RelativeLayout { public PreviewPlacerView(final Context context, final AttributeSet attrs) { super(context, attrs); setWillNotDraw(false); + } + public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { + if (!enabled) return; final Paint layerPaint = new Paint(); layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); setLayerType(LAYER_TYPE_HARDWARE, layerPaint); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 614c14308..719c7c81f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -76,6 +76,7 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.personalization.PersonalizationDictionaryHelper; +import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsActivity; @@ -170,6 +171,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mIsMainDictionaryAvailable; private UserBinaryDictionary mUserDictionary; private UserHistoryPredictionDictionary mUserHistoryPredictionDictionary; + private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary; private boolean mIsUserDictionaryAvailable; private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; @@ -560,6 +562,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mUserHistoryPredictionDictionary = PersonalizationDictionaryHelper .getUserHistoryPredictionDictionary(this, localeStr, prefs); newSuggest.setUserHistoryPredictionDictionary(mUserHistoryPredictionDictionary); + mPersonalizationPredictionDictionary = PersonalizationDictionaryHelper + .getPersonalizationPredictionDictionary(this, localeStr, prefs); + newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary); final Suggest oldSuggest = mSuggest; resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null); @@ -2509,9 +2514,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues currentSettings = mSettings.getCurrent(); if (!currentSettings.mCorrectionEnabled) return null; - final UserHistoryPredictionDictionary userHistoryDictionary = + final UserHistoryPredictionDictionary userHistoryPredictionDictionary = mUserHistoryPredictionDictionary; - if (userHistoryDictionary == null) return null; + if (userHistoryPredictionDictionary == null) return null; final String prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, 2); final String secondWord; @@ -2525,7 +2530,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int maxFreq = AutoCorrectionUtils.getMaxFrequency( suggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0); + userHistoryPredictionDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0); return prevWord; } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 6b016675a..2879e2e32 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -22,6 +22,7 @@ import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BoundedTreeSet; @@ -174,6 +175,12 @@ public final class Suggest { userHistoryPredictionDictionary); } + public void setPersonalizationPredictionDictionary( + final PersonalizationPredictionDictionary personalizationPredictionDictionary) { + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA, + personalizationPredictionDictionary); + } + public void setAutoCorrectionThreshold(float threshold) { mAutoCorrectionThreshold = threshold; } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java index f5dae99ef..9f013df1c 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java @@ -30,25 +30,52 @@ public class PersonalizationDictionaryHelper { private static final boolean DEBUG = false; private static final ConcurrentHashMap<String, SoftReference<UserHistoryPredictionDictionary>> - sLangDictCache = CollectionUtils.newConcurrentHashMap(); + sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap(); + + private static final ConcurrentHashMap<String, + SoftReference<PersonalizationPredictionDictionary>> + sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap(); public static UserHistoryPredictionDictionary getUserHistoryPredictionDictionary( final Context context, final String locale, final SharedPreferences sp) { - synchronized (sLangDictCache) { - if (sLangDictCache.containsKey(locale)) { + synchronized (sLangUserHistoryDictCache) { + if (sLangUserHistoryDictCache.containsKey(locale)) { final SoftReference<UserHistoryPredictionDictionary> ref = - sLangDictCache.get(locale); + sLangUserHistoryDictCache.get(locale); final UserHistoryPredictionDictionary dict = ref == null ? null : ref.get(); if (dict != null) { if (DEBUG) { - Log.w(TAG, "Use cached UserHistoryDictionary for " + locale); + Log.w(TAG, "Use cached UserHistoryPredictionDictionary for " + locale); } return dict; } } final UserHistoryPredictionDictionary dict = new UserHistoryPredictionDictionary(context, locale, sp); - sLangDictCache.put(locale, new SoftReference<UserHistoryPredictionDictionary>(dict)); + sLangUserHistoryDictCache.put( + locale, new SoftReference<UserHistoryPredictionDictionary>(dict)); + return dict; + } + } + + public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary( + final Context context, final String locale, final SharedPreferences sp) { + synchronized (sLangPersonalizationDictCache) { + if (sLangPersonalizationDictCache.containsKey(locale)) { + final SoftReference<PersonalizationPredictionDictionary> ref = + sLangPersonalizationDictCache.get(locale); + final PersonalizationPredictionDictionary dict = ref == null ? null : ref.get(); + if (dict != null) { + if (DEBUG) { + Log.w(TAG, "Use cached PersonalizationPredictionDictionary for " + locale); + } + return dict; + } + } + final PersonalizationPredictionDictionary dict = + new PersonalizationPredictionDictionary(context, locale, sp); + sLangPersonalizationDictCache.put( + locale, new SoftReference<PersonalizationPredictionDictionary>(dict)); return dict; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index fc8615b71..25187ced1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -1066,22 +1066,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT = new LogStatement("MotionEvent", true, false, "action", LogStatement.KEY_IS_LOGGING_RELATED, "motionEvent"); - public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action, - final long eventTime, final int index, final int id, final int x, final int y) { - if (me != null) { - final String actionString = LoggingUtils.getMotionEventActionTypeString(action); - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT, - actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me)); - if (action == MotionEvent.ACTION_DOWN) { - // Subtract 1 from eventTime so the down event is included in the later - // LogUnit, not the earlier (the test is for inequality). - researchLogger.setSavedDownEventTime(eventTime - 1); - } - // Refresh the timer in case we are capturing user feedback. - if (researchLogger.isMakingUserRecording()) { - researchLogger.resetRecordingTimer(); - } + public static void mainKeyboardView_processMotionEvent(final MotionEvent me) { + if (me == null) { + return; + } + final int action = me.getActionMasked(); + final long eventTime = me.getEventTime(); + final String actionString = LoggingUtils.getMotionEventActionTypeString(action); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT, + actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me)); + if (action == MotionEvent.ACTION_DOWN) { + // Subtract 1 from eventTime so the down event is included in the later + // LogUnit, not the earlier (the test is for inequality). + researchLogger.setSavedDownEventTime(eventTime - 1); + } + // Refresh the timer in case we are capturing user feedback. + if (researchLogger.isMakingUserRecording()) { + researchLogger.resetRecordingTimer(); } } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 771623c36..acd230ff2 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -72,7 +72,8 @@ LATIN_IME_CORE_SRC_FILES := \ suggest/core/session/dic_traverse_session.cpp \ $(addprefix suggest/policyimpl/dictionary/, \ dynamic_patricia_trie_policy.cpp \ - patricia_trie_policy.cpp) \ + patricia_trie_policy.cpp \ + patricia_trie_reading_utils.cpp) \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ $(addprefix suggest/policyimpl/typing/, \ scoring_params.cpp \ diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp index 52b668936..20b77b3b2 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp @@ -44,15 +44,15 @@ const int TaUtils::WHITELIST_SHORTCUT_PROBABILITY = 15; const int origin = *pos; switch (MASK_ATTRIBUTE_ADDRESS_TYPE & flags) { case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: - offset = ByteArrayUtils::readUint8andAdvancePosition( + offset = ByteArrayUtils::readUint8AndAdvancePosition( binaryDictionaryInfo->getDictRoot(), pos); break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: - offset = ByteArrayUtils::readUint16andAdvancePosition( + offset = ByteArrayUtils::readUint16AndAdvancePosition( binaryDictionaryInfo->getDictRoot(), pos); break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: - offset = ByteArrayUtils::readUint24andAdvancePosition( + offset = ByteArrayUtils::readUint24AndAdvancePosition( binaryDictionaryInfo->getDictRoot(), pos); break; } diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h index 15637d8a9..375fc7dff 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h @@ -33,7 +33,7 @@ class BinaryDictionaryTerminalAttributesReadingUtils { static AK_FORCE_INLINE TerminalAttributeFlags getFlagsAndForwardPointer( const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) { - return ByteArrayUtils::readUint8andAdvancePosition( + return ByteArrayUtils::readUint8AndAdvancePosition( binaryDictionaryInfo->getDictRoot(), pos); } @@ -66,7 +66,7 @@ class BinaryDictionaryTerminalAttributesReadingUtils { static AK_FORCE_INLINE int getShortcutListSizeAndForwardPointer( const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) { // readUint16andAdvancePosition() returns an offset *including* the uint16 field itself. - return ByteArrayUtils::readUint16andAdvancePosition( + return ByteArrayUtils::readUint16AndAdvancePosition( binaryDictionaryInfo->getDictRoot(), pos) - SHORTCUT_LIST_SIZE_FIELD_SIZE; } diff --git a/native/jni/src/suggest/core/dictionary/byte_array_utils.h b/native/jni/src/suggest/core/dictionary/byte_array_utils.h index daa822ffa..75ccfc766 100644 --- a/native/jni/src/suggest/core/dictionary/byte_array_utils.h +++ b/native/jni/src/suggest/core/dictionary/byte_array_utils.h @@ -50,39 +50,39 @@ class ByteArrayUtils { return buffer[pos]; } - static AK_FORCE_INLINE uint32_t readUint32andAdvancePosition( + static AK_FORCE_INLINE uint32_t readUint32AndAdvancePosition( const uint8_t *const buffer, int *const pos) { const uint32_t value = readUint32(buffer, *pos); *pos += 4; return value; } - static AK_FORCE_INLINE int readSint24andAdvancePosition( + static AK_FORCE_INLINE int readSint24AndAdvancePosition( const uint8_t *const buffer, int *const pos) { const uint8_t value = readUint8(buffer, *pos); if (value < 0x80) { - return readUint24andAdvancePosition(buffer, pos); + return readUint24AndAdvancePosition(buffer, pos); } else { (*pos)++; - return -(((value & 0x7F) << 16) ^ readUint16andAdvancePosition(buffer, pos)); + return -(((value & 0x7F) << 16) ^ readUint16AndAdvancePosition(buffer, pos)); } } - static AK_FORCE_INLINE uint32_t readUint24andAdvancePosition( + static AK_FORCE_INLINE uint32_t readUint24AndAdvancePosition( const uint8_t *const buffer, int *const pos) { const uint32_t value = readUint24(buffer, *pos); *pos += 3; return value; } - static AK_FORCE_INLINE uint16_t readUint16andAdvancePosition( + static AK_FORCE_INLINE uint16_t readUint16AndAdvancePosition( const uint8_t *const buffer, int *const pos) { const uint16_t value = readUint16(buffer, *pos); *pos += 2; return value; } - static AK_FORCE_INLINE uint8_t readUint8andAdvancePosition( + static AK_FORCE_INLINE uint8_t readUint8AndAdvancePosition( const uint8_t *const buffer, int *const pos) { return buffer[(*pos)++]; } @@ -113,7 +113,7 @@ class ByteArrayUtils { *pos += 1; return NOT_A_CODE_POINT; } else { - return readUint24andAdvancePosition(buffer, pos); + return readUint24AndAdvancePosition(buffer, pos); } } else { *pos += 1; diff --git a/native/jni/src/suggest/policyimpl/dictionary/binary_format.h b/native/jni/src/suggest/policyimpl/dictionary/binary_format.h index 9e22b50cd..23f4c7fec 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/binary_format.h +++ b/native/jni/src/suggest/policyimpl/dictionary/binary_format.h @@ -56,7 +56,6 @@ class BinaryFormat { // Mask and flags for attribute address type selection. static const int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30; - static bool hasBlacklistedOrNotAWordFlag(const int flags); static int getGroupCountAndForwardPointer(const uint8_t *const dict, int *pos); static uint8_t getFlagsAndForwardPointer(const uint8_t *const dict, int *pos); static int getCodePointAndForwardPointer(const uint8_t *const dict, int *pos); @@ -74,10 +73,6 @@ class BinaryFormat { static int getCodePointsAndProbabilityAndReturnCodePointCount( const uint8_t *const root, const int nodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability); - static int getBigramListPositionForWordPosition(const uint8_t *const root, - const int nodePosition); - static int getShortcutListPositionForWordPosition(const uint8_t *const root, - const int nodePosition); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryFormat); @@ -99,10 +94,6 @@ class BinaryFormat { static int skipBigrams(const uint8_t *const dict, const uint8_t flags, const int pos); }; -inline bool BinaryFormat::hasBlacklistedOrNotAWordFlag(const int flags) { - return (flags & (FLAG_IS_BLACKLISTED | FLAG_IS_NOT_A_WORD)) != 0; -} - AK_FORCE_INLINE int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t *const dict, int *pos) { const int msb = dict[(*pos)++]; @@ -475,38 +466,5 @@ AK_FORCE_INLINE int BinaryFormat::getCodePointsAndProbabilityAndReturnCodePointC return 0; } -AK_FORCE_INLINE int BinaryFormat::getBigramListPositionForWordPosition( - const uint8_t *const root, const int nodePosition) { - if (NOT_A_VALID_WORD_POS == nodePosition) return NOT_A_DICT_POS; - int position = nodePosition; - const uint8_t flags = getFlagsAndForwardPointer(root, &position); - if (!(flags & FLAG_HAS_BIGRAMS)) return NOT_A_DICT_POS; - if (flags & FLAG_HAS_MULTIPLE_CHARS) { - position = skipOtherCharacters(root, position); - } else { - getCodePointAndForwardPointer(root, &position); - } - position = skipProbability(flags, position); - position = skipChildrenPosition(flags, position); - position = skipShortcuts(root, flags, position); - return position; -} - -AK_FORCE_INLINE int BinaryFormat::getShortcutListPositionForWordPosition( - const uint8_t *const root, const int nodePosition) { - if (NOT_A_VALID_WORD_POS == nodePosition) return NOT_A_DICT_POS; - int position = nodePosition; - const uint8_t flags = getFlagsAndForwardPointer(root, &position); - if (!(flags & FLAG_HAS_SHORTCUT_TARGETS)) return NOT_A_DICT_POS; - if (flags & FLAG_HAS_MULTIPLE_CHARS) { - position = skipOtherCharacters(root, position); - } else { - getCodePointAndForwardPointer(root, &position); - } - position = skipProbability(flags, position); - position = skipChildrenPosition(flags, position); - return position; -} - } // namespace latinime #endif // LATINIME_BINARY_FORMAT_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp new file mode 100644 index 000000000..0de6341b0 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" + +#include "defines.h" +#include "suggest/core/dictionary/byte_array_utils.h" + +namespace latinime { + +typedef DynamicPatriciaTrieReadingUtils DptReadingUtils; + +const DptReadingUtils::NodeFlags DptReadingUtils::MASK_MOVED = 0xC0; +const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_NOT_MOVED = 0xC0; +const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_MOVED = 0x40; +const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_DELETED = 0x80; + +/* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition( + const uint8_t *const buffer, const NodeFlags flags, int *const pos) { + if ((flags & MASK_MOVED) == FLAG_IS_NOT_MOVED) { + const int base = *pos; + return base + ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); + } else { + return NOT_A_DICT_POS; + } +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h new file mode 100644 index 000000000..f44c2651a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H +#define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/core/dictionary/byte_array_utils.h" + +namespace latinime { + +class DynamicPatriciaTrieReadingUtils { + public: + typedef uint8_t NodeFlags; + + static AK_FORCE_INLINE int getForwardLinkPosition(const uint8_t *const buffer, const int pos) { + int linkAddressPos = pos; + return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos); + } + + static AK_FORCE_INLINE bool isValidForwardLinkPosition(const int forwardLinkAddress) { + return forwardLinkAddress != 0; + } + + static AK_FORCE_INLINE int getParentPosAndAdvancePosition(const uint8_t *const buffer, + int *const pos) { + const int base = *pos; + return base + ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); + } + + static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer, + const NodeFlags flags, int *const pos); + + /** + * Node Flags + */ + static AK_FORCE_INLINE bool isMoved(const NodeFlags flags) { + return FLAG_IS_MOVED == (MASK_MOVED & flags); + } + + static AK_FORCE_INLINE bool isDeleted(const NodeFlags flags) { + return FLAG_IS_DELETED == (MASK_MOVED & flags); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils); + + static const NodeFlags MASK_MOVED; + static const NodeFlags FLAG_IS_NOT_MOVED; + static const NodeFlags FLAG_IS_MOVED; + static const NodeFlags FLAG_IS_DELETED; +}; +} // namespace latinime +#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp index 2a9a5ce7a..097f7c86a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp @@ -21,7 +21,9 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/binary_dictionary_info.h" +#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h" #include "suggest/policyimpl/dictionary/binary_format.h" +#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" namespace latinime { @@ -34,7 +36,7 @@ void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, return; } int nextPos = dicNode->getChildrenPos(); - const int childCount = BinaryFormat::getGroupCountAndForwardPointer( + const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition( binaryDictionaryInfo->getDictRoot(), &nextPos); for (int i = 0; i < childCount; i++) { nextPos = createAndGetLeavingChildNode(dicNode, nextPos, binaryDictionaryInfo, @@ -60,82 +62,108 @@ int PatriciaTriePolicy::getTerminalNodePositionOfWord( int PatriciaTriePolicy::getUnigramProbability( const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) const { - const uint8_t *const root = binaryDictionaryInfo->getDictRoot(); + if (nodePos == NOT_A_VALID_WORD_POS) { + return NOT_A_PROBABILITY; + } + const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot(); int pos = nodePos; - const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos); - if (flags & (BinaryFormat::FLAG_IS_BLACKLISTED | BinaryFormat::FLAG_IS_NOT_A_WORD)) { + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos); + if (!PatriciaTrieReadingUtils::isTerminal(flags)) { + return NOT_A_PROBABILITY; + } + if (PatriciaTrieReadingUtils::isNotAWord(flags) + || PatriciaTrieReadingUtils::isBlacklisted(flags)) { // If this is not a word, or if it's a blacklisted entry, it should behave as // having no probability outside of the suggestion process (where it should be used // for shortcuts). return NOT_A_PROBABILITY; } - const bool hasMultipleChars = (0 != (BinaryFormat::FLAG_HAS_MULTIPLE_CHARS & flags)); - if (hasMultipleChars) { - pos = BinaryFormat::skipOtherCharacters(root, pos); - } else { - BinaryFormat::getCodePointAndForwardPointer(root, &pos); - } - return BinaryFormat::readProbabilityWithoutMovingPointer(root, pos); + PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos); + return PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos); } int PatriciaTriePolicy::getShortcutPositionOfNode( const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) const { - return BinaryFormat::getShortcutListPositionForWordPosition( - binaryDictionaryInfo->getDictRoot(), nodePos); + if (nodePos == NOT_A_VALID_WORD_POS) { + return NOT_A_DICT_POS; + } + const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot(); + int pos = nodePos; + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos); + if (!PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { + return NOT_A_DICT_POS; + } + PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos); + if (PatriciaTrieReadingUtils::isTerminal(flags)) { + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos); + } + if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) { + PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(dictRoot, flags, &pos); + } + return pos; } int PatriciaTriePolicy::getBigramsPositionOfNode( const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) const { - return BinaryFormat::getBigramListPositionForWordPosition( - binaryDictionaryInfo->getDictRoot(), nodePos); + if (nodePos == NOT_A_VALID_WORD_POS) { + return NOT_A_DICT_POS; + } + const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot(); + int pos = nodePos; + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos); + if (!PatriciaTrieReadingUtils::hasBigrams(flags)) { + return NOT_A_DICT_POS; + } + PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos); + if (PatriciaTrieReadingUtils::isTerminal(flags)) { + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos); + } + if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) { + PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(dictRoot, flags, &pos); + } + if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { + BinaryDictionaryTerminalAttributesReadingUtils::skipShortcuts(binaryDictionaryInfo, &pos); + } + return pos; } -int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNode, int pos, - const BinaryDictionaryInfo *const binaryDictionaryInfo, +int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNode, + const int nodePos, const BinaryDictionaryInfo *const binaryDictionaryInfo, const NodeFilter *const childrenFilter, DicNodeVector *childDicNodes) const { - const int nextPos = pos; - const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer( - binaryDictionaryInfo->getDictRoot(), &pos); - const bool hasMultipleChars = (0 != (BinaryFormat::FLAG_HAS_MULTIPLE_CHARS & flags)); - const bool isTerminal = (0 != (BinaryFormat::FLAG_IS_TERMINAL & flags)); - const bool hasChildren = BinaryFormat::hasChildrenInFlags(flags); - const bool isBlacklistedOrNotAWord = BinaryFormat::hasBlacklistedOrNotAWordFlag(flags); - - int codePoint = BinaryFormat::getCodePointAndForwardPointer( - binaryDictionaryInfo->getDictRoot(), &pos); - ASSERT(NOT_A_CODE_POINT != codePoint); - // TODO: optimize this + const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot(); + int pos = nodePos; + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos); int mergedNodeCodePoints[MAX_WORD_LENGTH]; - uint16_t mergedNodeCodePointCount = 0; - mergedNodeCodePoints[mergedNodeCodePointCount++] = codePoint; - - do { - const int nextCodePoint = hasMultipleChars - ? BinaryFormat::getCodePointAndForwardPointer( - binaryDictionaryInfo->getDictRoot(), &pos) : NOT_A_CODE_POINT; - const bool isLastChar = (NOT_A_CODE_POINT == nextCodePoint); - if (!isLastChar) { - mergedNodeCodePoints[mergedNodeCodePointCount++] = nextCodePoint; - } - codePoint = nextCodePoint; - } while (NOT_A_CODE_POINT != codePoint); - - const int probability = isTerminal ? BinaryFormat::readProbabilityWithoutMovingPointer( - binaryDictionaryInfo->getDictRoot(), pos) : NOT_A_PROBABILITY; - pos = BinaryFormat::skipProbability(flags, pos); - int childrenPos = hasChildren ? BinaryFormat::readChildrenPosition( - binaryDictionaryInfo->getDictRoot(), flags, pos) : NOT_A_DICT_POS; - const int siblingPos = BinaryFormat::skipChildrenPosAndAttributes( - binaryDictionaryInfo->getDictRoot(), flags, pos); - - if (childrenFilter->isFilteredOut(mergedNodeCodePoints[0])) { - return siblingPos; + const int mergedNodeCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( + dictRoot, flags, MAX_WORD_LENGTH, mergedNodeCodePoints, &pos); + const int probability = (PatriciaTrieReadingUtils::isTerminal(flags))? + PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos) + : NOT_A_PROBABILITY; + const int childrenPos = PatriciaTrieReadingUtils::hasChildrenInFlags(flags) ? + PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( + dictRoot, flags, &pos) : NOT_A_DICT_POS; + if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { + BinaryDictionaryTerminalAttributesReadingUtils::skipShortcuts(binaryDictionaryInfo, &pos); + } + if (PatriciaTrieReadingUtils::hasBigrams(flags)) { + BinaryDictionaryTerminalAttributesReadingUtils::skipExistingBigrams( + binaryDictionaryInfo, &pos); + } + if (!childrenFilter->isFilteredOut(mergedNodeCodePoints[0])) { + childDicNodes->pushLeavingChild(dicNode, nodePos, childrenPos, probability, + PatriciaTrieReadingUtils::isTerminal(flags), + PatriciaTrieReadingUtils::hasChildrenInFlags(flags), + PatriciaTrieReadingUtils::isBlacklisted(flags) || + PatriciaTrieReadingUtils::isNotAWord(flags), + mergedNodeCodePointCount, mergedNodeCodePoints); } - childDicNodes->pushLeavingChild(dicNode, nextPos, childrenPos, probability, isTerminal, - hasChildren, isBlacklistedOrNotAWord, mergedNodeCodePointCount, mergedNodeCodePoints); - return siblingPos; + return pos; } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h index 42827d93a..71f256eee 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h @@ -61,7 +61,7 @@ class PatriciaTriePolicy : public DictionaryStructurePolicy { PatriciaTriePolicy() {} ~PatriciaTriePolicy() {} - int createAndGetLeavingChildNode(const DicNode *const dicNode, int pos, + int createAndGetLeavingChildNode(const DicNode *const dicNode, const int nodePos, const BinaryDictionaryInfo *const binaryDictionaryInfo, const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const; }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp new file mode 100644 index 000000000..89e981df8 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" + +#include "defines.h" +#include "suggest/core/dictionary/byte_array_utils.h" + +namespace latinime { + +typedef PatriciaTrieReadingUtils PtReadingUtils; + +const PtReadingUtils::NodeFlags PtReadingUtils::MASK_GROUP_ADDRESS_TYPE = 0xC0; +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00; +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40; +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80; +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0; + +// Flag for single/multiple char group +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_MULTIPLE_CHARS = 0x20; +// Flag for terminal groups +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_TERMINAL = 0x10; +// Flag for shortcut targets presence +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_SHORTCUT_TARGETS = 0x08; +// Flag for bigram presence +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_BIGRAMS = 0x04; +// Flag for non-words (typically, shortcut only entries) +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_NOT_A_WORD = 0x02; +// Flag for blacklist +const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_BLACKLISTED = 0x01; + +/* static */ int PtReadingUtils::readChildrenPositionAndAdvancePosition( + const uint8_t *const buffer, const NodeFlags flags, int *const pos) { + const int base = *pos; + int offset = 0; + switch (MASK_GROUP_ADDRESS_TYPE & flags) { + case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: + offset = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos); + break; + case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: + offset = ByteArrayUtils::readUint16AndAdvancePosition(buffer, pos); + break; + case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: + offset = ByteArrayUtils::readUint24AndAdvancePosition(buffer, pos); + break; + default: + // If we come here, it means we asked for the children of a word with + // no children. + return NOT_A_DICT_POS; + } + return base + offset; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h new file mode 100644 index 000000000..002c3f19b --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h @@ -0,0 +1,139 @@ +/* + * 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. + */ + +#ifndef LATINIME_PATRICIA_TRIE_READING_UTILS_H +#define LATINIME_PATRICIA_TRIE_READING_UTILS_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/core/dictionary/byte_array_utils.h" + +namespace latinime { + +class PatriciaTrieReadingUtils { + public: + typedef uint8_t NodeFlags; + + static AK_FORCE_INLINE int getGroupCountAndAdvancePosition( + const uint8_t *const buffer, int *const pos) { + const uint8_t firstByte = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos); + if (firstByte < 0x80) { + return firstByte; + } else { + return ((firstByte & 0x7F) << 8) ^ ByteArrayUtils::readUint8AndAdvancePosition( + buffer, pos); + } + } + + static AK_FORCE_INLINE NodeFlags getFlagsAndAdvancePosition(const uint8_t *const buffer, + int *const pos) { + return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos); + } + + static AK_FORCE_INLINE int getCodePointAndAdvancePosition(const uint8_t *const buffer, + int *const pos) { + return ByteArrayUtils::readCodePointAndAdvancePosition(buffer, pos); + } + + // Returns the number of read characters. + static AK_FORCE_INLINE int getCharsAndAdvancePosition(const uint8_t *const buffer, + const NodeFlags flags, const int maxLength, int *const outBuffer, int *const pos) { + int length = 0; + if (hasMultipleChars(flags)) { + length = ByteArrayUtils::readStringAndAdvancePosition(buffer, maxLength, outBuffer, + pos); + } else { + if (maxLength > 0) { + outBuffer[0] = getCodePointAndAdvancePosition(buffer, pos); + length = 1; + } + } + return length; + } + + // Returns the number of skipped characters. + static AK_FORCE_INLINE int skipCharacters(const uint8_t *const buffer, const NodeFlags flags, + const int maxLength, int *const pos) { + if (hasMultipleChars(flags)) { + return ByteArrayUtils::advancePositionToBehindString(buffer, maxLength, pos); + } else { + if (maxLength > 0) { + getCodePointAndAdvancePosition(buffer, pos); + return 1; + } else { + return 0; + } + } + } + + static AK_FORCE_INLINE int readProbabilityAndAdvancePosition(const uint8_t *const buffer, + int *const pos) { + return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos); + } + + static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer, + const NodeFlags flags, int *const pos); + + /** + * Node Flags + */ + static AK_FORCE_INLINE bool isBlacklisted(const NodeFlags flags) { + return (flags & FLAG_IS_BLACKLISTED) != 0; + } + + static AK_FORCE_INLINE bool isNotAWord(const NodeFlags flags) { + return (flags & FLAG_IS_NOT_A_WORD) != 0; + } + + static AK_FORCE_INLINE bool isTerminal(const NodeFlags flags) { + return (flags & FLAG_IS_TERMINAL) != 0; + } + + static AK_FORCE_INLINE bool hasShortcutTargets(const NodeFlags flags) { + return (flags & FLAG_HAS_SHORTCUT_TARGETS) != 0; + } + + static AK_FORCE_INLINE bool hasBigrams(const NodeFlags flags) { + return (flags & FLAG_HAS_BIGRAMS) != 0; + } + + static AK_FORCE_INLINE bool hasMultipleChars(const NodeFlags flags) { + return (flags & FLAG_HAS_MULTIPLE_CHARS) != 0; + } + + static AK_FORCE_INLINE bool hasChildrenInFlags(const NodeFlags flags) { + return FLAG_GROUP_ADDRESS_TYPE_NOADDRESS != (MASK_GROUP_ADDRESS_TYPE & flags); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTrieReadingUtils); + + static const NodeFlags MASK_GROUP_ADDRESS_TYPE; + static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_NOADDRESS; + static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; + static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; + static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; + + static const NodeFlags FLAG_HAS_MULTIPLE_CHARS; + static const NodeFlags FLAG_IS_TERMINAL; + static const NodeFlags FLAG_HAS_SHORTCUT_TARGETS; + static const NodeFlags FLAG_HAS_BIGRAMS; + static const NodeFlags FLAG_IS_NOT_A_WORD; + static const NodeFlags FLAG_IS_BLACKLISTED; +}; +} // namespace latinime +#endif /* LATINIME_PATRICIA_TRIE_NODE_READING_UTILS_H */ |