From 73779f7631d41f16f89c62cae09a1b27d8189dc3 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Wed, 19 Sep 2012 18:00:32 +0900 Subject: Make readUnigramsAndBigramsBinary read linked-list nodes. Change-Id: I07ae036b0b06e71d7a18f2bf11e4692cd4213568 --- .../inputmethod/latin/makedict/BinaryDictIOUtils.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 7a1b9dcb7..406071071 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -92,7 +92,18 @@ public class BinaryDictIOUtils { } if (p.mPosition == p.mNumOfCharGroup) { - stack.pop(); + if (formatOptions.mHasLinkedListNode) { + final int forwardLinkAddress = buffer.readUnsignedInt24(); + if (forwardLinkAddress != FormatSpec.NO_FORWARD_LINK_ADDRESS) { + // the node has a forward link. + p.mNumOfCharGroup = Position.NOT_READ_GROUPCOUNT; + p.mAddress = forwardLinkAddress; + } else { + stack.pop(); + } + } else { + stack.pop(); + } } else { // the node has more groups. p.mAddress = buffer.position(); -- cgit v1.2.3-83-g751a From 66597f5e5f3249f418665c1990fb539d2f5565d5 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Thu, 20 Sep 2012 14:27:33 +0900 Subject: Add deleteWord. bug: 6669677 Change-Id: I1a5b90ee05e5cffd74a5c140384a3e37c79e7e70 --- .../latin/makedict/BinaryDictIOUtils.java | 22 +++++++++++++ .../latin/makedict/BinaryDictInputOutput.java | 2 +- .../latin/makedict/BinaryDictIOTests.java | 37 +++++++++++++++++++++- 3 files changed, 59 insertions(+), 2 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 406071071..397532933 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -201,4 +201,26 @@ public class BinaryDictIOUtils { } return FormatSpec.NOT_VALID_WORD; } + + /** + * Delete the word from the binary file. + * + * @param buffer the buffer to write. + * @param word the word we delete + * @throws IOException + * @throws UnsupportedFormatException + */ + public static void deleteWord(final FusionDictionaryBufferInterface buffer, + final String word) throws IOException, UnsupportedFormatException { + buffer.position(0); + final FileHeader header = BinaryDictInputOutput.readHeader(buffer); + final int wordPosition = getTerminalPosition(buffer, word); + if (wordPosition == FormatSpec.NOT_VALID_WORD) return; + + buffer.position(wordPosition); + final int flags = buffer.readUnsignedByte(); + final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL; + buffer.position(wordPosition); + buffer.put((byte)newFlags); + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 1d3e94bb7..7b8dc5cc5 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -392,7 +392,7 @@ public class BinaryDictInputOutput { /** * Helper method to check whether the CharGroup has a parent address. */ - private static boolean hasParentAddress(final FormatOptions options) { + public static boolean hasParentAddress(final FormatOptions options) { return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_PARENT_ADDRESS && options.mHasParentAddress; } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java index 24776d536..539021f24 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java @@ -25,6 +25,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.Node; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import android.test.AndroidTestCase; +import android.test.MoreAsserts; import android.util.Log; import android.util.SparseArray; @@ -517,7 +518,7 @@ public class BinaryDictIOTests extends AndroidTestCase { public void testGetTerminalPosition() { File file = null; try { - file = File.createTempFile("runReadUnigrams", ".dict"); + file = File.createTempFile("testGetTerminalPosition", ".dict"); } catch (IOException e) { // do nothing } @@ -564,4 +565,38 @@ public class BinaryDictIOTests extends AndroidTestCase { runGetTerminalPosition(buffer, word, i, false); } } + + public void testDeleteWord() { + File file = null; + try { + file = File.createTempFile("testGetTerminalPosition", ".dict"); + } catch (IOException e) { + // do nothing + } + assertNotNull(file); + + final FusionDictionary dict = new FusionDictionary(new Node(), + new FusionDictionary.DictionaryOptions( + new HashMap(), false, false)); + addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); + timeWritingDictToFile(file, dict, VERSION3_WITH_LINKEDLIST_NODE); + + final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY); + + try { + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0))); + BinaryDictIOUtils.deleteWord(buffer, sWords.get(0)); + assertEquals(FormatSpec.NOT_VALID_WORD, + BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0))); + + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5))); + BinaryDictIOUtils.deleteWord(buffer, sWords.get(5)); + assertEquals(FormatSpec.NOT_VALID_WORD, + BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5))); + } catch (IOException e) { + } catch (UnsupportedFormatException e) { + } + } } -- cgit v1.2.3-83-g751a From 8ae8c761493867be7364806cdc4d7103a46dc181 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 21 Sep 2012 15:09:47 +0900 Subject: Cleanup settings bug: 7200189 Change-Id: Ibfd9254670cde5f4b2b693cc9334ee2845bede59 --- java/res/xml/prefs.xml | 10 +++--- java/res/xml/prefs_for_debug.xml | 6 ++++ .../android/inputmethod/latin/DebugSettings.java | 11 ++++++ .../com/android/inputmethod/latin/Settings.java | 41 +++++++--------------- .../android/inputmethod/latin/SettingsValues.java | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) (limited to 'java/src') diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 3e83fc0fc..478dc0f80 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -92,11 +92,6 @@ android:summary="@string/gesture_input_summary" android:persistent="true" android:defaultValue="true" /> - + diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml index b926ed065..605a02f07 100644 --- a/java/res/xml/prefs_for_debug.xml +++ b/java/res/xml/prefs_for_debug.xml @@ -48,4 +48,10 @@ android:persistent="true" android:defaultValue="false" /> + + diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index af7649863..1ea14dad5 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -23,10 +23,12 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.Process; import android.preference.CheckBoxPreference; +import android.preference.Preference; import android.preference.PreferenceFragment; import android.util.Log; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.research.ResearchLogger; public class DebugSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -34,6 +36,7 @@ public class DebugSettings extends PreferenceFragment private static final String TAG = DebugSettings.class.getSimpleName(); private static final String DEBUG_MODE_KEY = "debug_mode"; public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch"; + public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; private boolean mServiceNeedsRestart = false; private CheckBoxPreference mDebugMode; @@ -45,6 +48,14 @@ public class DebugSettings extends PreferenceFragment SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); prefs.registerOnSharedPreferenceChangeListener(this); + final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE); + if (usabilityStudyPref instanceof CheckBoxPreference) { + final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; + checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, + ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); + checkbox.setSummary(R.string.settings_warning_researcher_mode); + } + mServiceNeedsRestart = false; mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY); updateDebugMode(); diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 9479a88a7..b984ec367 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -39,12 +39,10 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; public final class Settings extends InputMethodSettingsFragment implements SharedPreferences.OnSharedPreferenceChangeListener { - public static final boolean ENABLE_INTERNAL_SETTINGS = ProductionFlag.IS_INTERNAL; // In the same order as xml/prefs.xml public static final String PREF_GENERAL_SETTINGS = "general_settings"; @@ -58,7 +56,6 @@ public final class Settings extends InputMethodSettingsFragment public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; public static final String PREF_MISC_SETTINGS = "misc_settings"; - public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = "last_user_dictionary_write_time"; public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; @@ -133,14 +130,6 @@ public final class Settings extends InputMethodSettingsFragment mAutoCorrectionThresholdPreference = (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); - mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); - if (mDebugSettingsPreference != null) { - final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); - debugSettingsIntent.setClassName( - context.getPackageName(), DebugSettings.class.getName()); - mDebugSettingsPreference.setIntent(debugSettingsIntent); - } - ensureConsistencyOfAutoCorrectionSettings(); final PreferenceGroup generalSettings = @@ -150,6 +139,18 @@ public final class Settings extends InputMethodSettingsFragment final PreferenceGroup miscSettings = (PreferenceGroup) findPreference(PREF_MISC_SETTINGS); + mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); + if (mDebugSettingsPreference != null) { + if (ProductionFlag.IS_INTERNAL) { + final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); + debugSettingsIntent.setClassName( + context.getPackageName(), DebugSettingsActivity.class.getName()); + mDebugSettingsPreference.setIntent(debugSettingsIntent); + } else { + miscSettings.removePreference(mDebugSettingsPreference); + } + } + final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { @@ -218,24 +219,6 @@ public final class Settings extends InputMethodSettingsFragment setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser); } - final boolean showUsabilityStudyModeOption = - res.getBoolean(R.bool.config_enable_usability_study_mode_option) - || ProductionFlag.IS_EXPERIMENTAL || ENABLE_INTERNAL_SETTINGS; - final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE); - if (!showUsabilityStudyModeOption) { - if (usabilityStudyPref != null) { - miscSettings.removePreference(usabilityStudyPref); - } - } - if (ProductionFlag.IS_EXPERIMENTAL) { - if (usabilityStudyPref instanceof CheckBoxPreference) { - CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; - checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, - ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); - checkbox.setSummary(R.string.settings_warning_researcher_mode); - } - } - mKeypressVibrationDurationSettingsPref = (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); if (mKeypressVibrationDurationSettingsPref != null) { diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 9d8379a7a..5b8f1cfb4 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -410,7 +410,7 @@ public final class SettingsValues { // Likewise public static boolean getUsabilityStudyMode(final SharedPreferences prefs) { // TODO: use mUsabilityStudyMode instead of reading it again here - return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true); + return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); } public static long getLastUserHistoryWriteTime(final SharedPreferences prefs, -- cgit v1.2.3-83-g751a From 02a67200fc25d1be9dfbc35e3bb4b59bef28f386 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 21 Sep 2012 12:15:02 +0900 Subject: Fix gesture start detection algorithm Bug: 7032858 Change-Id: I9f4d939fa87fdead4c5a5921338a16cd0a59b7ac --- .../inputmethod/keyboard/PointerTracker.java | 10 +- .../keyboard/internal/GestureStroke.java | 161 +++++++++++++++------ .../internal/GestureStrokeWithPreviewPoints.java | 15 +- .../inputmethod/latin/BinaryDictionary.java | 2 +- .../inputmethod/latin/UserHistoryDictionary.java | 2 +- 5 files changed, 127 insertions(+), 63 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 03d610a39..d4902ec28 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -688,7 +688,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { mIsDetectingGesture = true; final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime); mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown, - false /* isHistorical */); + true /* isMajorEvent */); } private void onDownEventInternal(final int x, final int y, final long eventTime) { @@ -724,10 +724,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { } private void onGestureMoveEvent(final int x, final int y, final long eventTime, - final boolean isHistorical, final Key key) { + final boolean isMajorEvent, final Key key) { final int gestureTime = (int)(eventTime - sGestureFirstDownTime); if (mIsDetectingGesture) { - mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isHistorical); + mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); mayStartBatchInput(); if (sInGesture && key != null) { updateBatchInput(eventTime); @@ -752,7 +752,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int historicalY = (int)me.getHistoricalY(pointerIndex, h); final long historicalTime = me.getHistoricalEventTime(h); onGestureMoveEvent(historicalX, historicalY, historicalTime, - true /* isHistorical */, null); + false /* isMajorEvent */, null); } } @@ -767,7 +767,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (sShouldHandleGesture) { // Register move event on gesture tracker. - onGestureMoveEvent(x, y, eventTime, false /* isHistorical */, key); + onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key); if (sInGesture) { mIgnoreModifierKey = true; mTimerProxy.cancelLongPressTimer(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index f0be0ee94..73413f698 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -14,34 +14,45 @@ package com.android.inputmethod.keyboard.internal; +import android.util.Log; + import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.ResizableIntArray; public class GestureStroke { + private static final String TAG = GestureStroke.class.getSimpleName(); + private static final boolean DEBUG = false; + public static final int DEFAULT_CAPACITY = 128; private final int mPointerId; private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); - private float mLength; private int mIncrementalRecognitionSize; private int mLastIncrementalBatchSize; - private long mLastPointTime; - private int mLastPointX; - private int mLastPointY; - - private int mMinGestureLength; // pixel - private int mMinGestureSampleLength; // pixel - private int mGestureRecognitionThreshold; // pixel / sec + private long mLastMajorEventTime; + private int mLastMajorEventX; + private int mLastMajorEventY; + + private int mKeyWidth; + private int mStartGestureLengthThreshold; // pixel + private int mMinGestureSamplingLength; // pixel + private int mGestureRecognitionSpeedThreshold; // pixel / sec + private int mDetectFastMoveSpeedThreshold; // pixel /sec + private int mDetectFastMoveTime; + private int mDetectFastMoveX; + private int mDetectFastMoveY; // TODO: Move some of these to resource. - private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 0.75f; - private static final int MIN_GESTURE_START_DURATION = 100; // msec + private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.75f; + private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f; private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH = 5.5f; // keyWidth / sec + private static final float DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH = + 5.0f; // keyWidth / sec private static final int MSEC_PER_SEC = 1000; public static final boolean hasRecognitionTimePast( @@ -54,64 +65,120 @@ public class GestureStroke { } public void setKeyboardGeometry(final int keyWidth) { + mKeyWidth = keyWidth; // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key? - mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH); - mMinGestureSampleLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH); - mGestureRecognitionThreshold = + mStartGestureLengthThreshold = + (int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH); + mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH); + mGestureRecognitionSpeedThreshold = (int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH); + mDetectFastMoveSpeedThreshold = + (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH); + if (DEBUG) { + Log.d(TAG, "setKeyboardGeometry: keyWidth=" + keyWidth); + } } public boolean isStartOfAGesture() { + if (mDetectFastMoveTime == 0) { + return false; + } final int size = mEventTimes.getLength(); - final int downDuration = (size > 0) ? mEventTimes.get(size - 1) : 0; - return downDuration > MIN_GESTURE_START_DURATION && mLength > mMinGestureLength; + if (size <= 0) { + return false; + } + final int lastIndex = size - 1; + final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime; + final int deltaLength = getDistance( + mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex), + mDetectFastMoveX, mDetectFastMoveY); + final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD + && deltaLength > mStartGestureLengthThreshold; + if (DEBUG) { + Log.d(TAG, "isStartOfAGesture: dT=" + deltaTime + " dL=" + deltaLength + + " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : "")); + } + return isStartOfAGesture; } public void reset() { - mLength = 0; mIncrementalRecognitionSize = 0; mLastIncrementalBatchSize = 0; - mLastPointTime = 0; mEventTimes.setLength(0); mXCoordinates.setLength(0); mYCoordinates.setLength(0); + mLastMajorEventTime = 0; + mDetectFastMoveTime = 0; + } + + private void appendPoint(final int x, final int y, final int time) { + mEventTimes.add(time); + mXCoordinates.add(x); + mYCoordinates.add(y); } - public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { - final boolean needsSampling; + private void updateMajorEvent(final int x, final int y, final int time) { + mLastMajorEventTime = time; + mLastMajorEventX = x; + mLastMajorEventY = y; + } + + private int detectFastMove(final int x, final int y, final int time) { final int size = mEventTimes.getLength(); - if (size == 0) { - needsSampling = true; - } else { - final int lastIndex = size - 1; - final int lastX = mXCoordinates.get(lastIndex); - final int lastY = mYCoordinates.get(lastIndex); - final float dist = getDistance(lastX, lastY, x, y); - needsSampling = dist > mMinGestureSampleLength; - mLength += dist; + final int lastIndex = size - 1; + final int lastX = mXCoordinates.get(lastIndex); + final int lastY = mYCoordinates.get(lastIndex); + final int dist = getDistance(lastX, lastY, x, y); + final int msecs = time - mEventTimes.get(lastIndex); + if (msecs > 0) { + final int pixels = getDistance(lastX, lastY, x, y); + final int pixelsPerSec = pixels * MSEC_PER_SEC; + if (DEBUG) { + final float speed = (float)pixelsPerSec / msecs / mKeyWidth; + Log.d(TAG, String.format("Speed=%.3f keyWidth/sec", speed)); + } + // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC) + if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) { + if (DEBUG) { + Log.d(TAG, "Detect fast move: T=" + time + " points = " + size); + } + mDetectFastMoveTime = time; + mDetectFastMoveX = x; + mDetectFastMoveY = y; + } } - if (needsSampling) { - mEventTimes.add(time); - mXCoordinates.add(x); - mYCoordinates.add(y); + return dist; + } + + public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) { + final int size = mEventTimes.getLength(); + if (size <= 0) { + // Down event + appendPoint(x, y, time); + updateMajorEvent(x, y, time); + } else { + final int dist = detectFastMove(x, y, time); + if (dist > mMinGestureSamplingLength) { + appendPoint(x, y, time); + } } - if (!isHistorical) { + if (isMajorEvent) { updateIncrementalRecognitionSize(x, y, time); + updateMajorEvent(x, y, time); } } private void updateIncrementalRecognitionSize(final int x, final int y, final int time) { - final int msecs = (int)(time - mLastPointTime); - if (msecs > 0) { - final int pixels = (int)getDistance(mLastPointX, mLastPointY, x, y); - // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC) - if (pixels * MSEC_PER_SEC < mGestureRecognitionThreshold * msecs) { - mIncrementalRecognitionSize = mEventTimes.getLength(); - } + final int msecs = (int)(time - mLastMajorEventTime); + if (msecs <= 0) { + return; + } + final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y); + final int pixelsPerSec = pixels * MSEC_PER_SEC; + // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC) + if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) { + mIncrementalRecognitionSize = mEventTimes.getLength(); } - mLastPointTime = time; - mLastPointX = x; - mLastPointY = y; } public void appendAllBatchPoints(final InputPointers out) { @@ -132,11 +199,11 @@ public class GestureStroke { mLastIncrementalBatchSize = size; } - private static float getDistance(final int x1, final int y1, final int x2, final int y2) { - final float dx = x1 - x2; - final float dy = y1 - y2; + private static int getDistance(final int x1, final int y1, final int x2, final int y2) { + final int dx = x1 - x2; + final int dy = y1 - y2; // Note that, in recent versions of Android, FloatMath is actually slower than // java.lang.Math due to the way the JIT optimizes java.lang.Math. - return (float)Math.sqrt(dx * dx + dy * dy); + return (int)Math.sqrt(dx * dx + dy * dy); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java index ce3914076..3487b5018 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java @@ -65,21 +65,18 @@ public class GestureStrokeWithPreviewPoints extends GestureStroke { private boolean needsSampling(final int x, final int y) { final int dx = x - mLastX; final int dy = y - mLastY; - final boolean needsSampling = (dx * dx + dy * dy >= mMinPreviewSampleLengthSquare); - if (needsSampling) { - mLastX = x; - mLastY = y; - } - return needsSampling; + return dx * dx + dy * dy >= mMinPreviewSampleLengthSquare; } @Override - public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { - super.addPoint(x, y, time, isHistorical); - if (mPreviewEventTimes.getLength() == 0 || isHistorical || needsSampling(x, y)) { + public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) { + super.addPoint(x, y, time, isMajorEvent); + if (isMajorEvent || needsSampling(x, y)) { mPreviewEventTimes.add(time); mPreviewXCoordinates.add(x); mPreviewYCoordinates.add(y); + mLastX = x; + mLastY = y; } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index c3ae81f3a..9244f16b1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -45,7 +45,7 @@ public class BinaryDictionary extends Dictionary { public static final int MAX_WORDS = 18; public static final int MAX_SPACES = 16; - private static final String TAG = "BinaryDictionary"; + private static final String TAG = BinaryDictionary.class.getSimpleName(); private static final int MAX_PREDICTIONS = 60; private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index 683ee4f5c..e03af649c 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantLock; * cancellation or manual picks. This allows the keyboard to adapt to the typist over time. */ public class UserHistoryDictionary extends ExpandableDictionary { - private static final String TAG = "UserHistoryDictionary"; + private static final String TAG = UserHistoryDictionary.class.getSimpleName(); public static final boolean DBG_SAVE_RESTORE = false; public static final boolean DBG_STRESS_TEST = false; public static final boolean DBG_ALWAYS_WRITE = false; -- cgit v1.2.3-83-g751a From 9f09c6fbc81ed40a41c0a23bd5913214ec7f6a9b Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 24 Sep 2012 11:39:37 +0900 Subject: Disable gesture input while fast typing Bug: 7032858 Change-Id: I0f9f92c712dbe45f4ba0b3cf331fbe11426c1db4 --- java/res/values/attrs.xml | 2 ++ java/res/values/config.xml | 1 + java/res/values/styles.xml | 1 + .../android/inputmethod/keyboard/MainKeyboardView.java | 15 +++++++++++++++ .../android/inputmethod/keyboard/PointerTracker.java | 17 +++++++++++++++++ .../inputmethod/keyboard/internal/GestureStroke.java | 2 +- 6 files changed, 37 insertions(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 7e8c77e13..acc5df2e5 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -125,6 +125,8 @@ + + diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 8e2d43e4e..1f6adffbb 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -66,6 +66,7 @@ 1200 350 + 350 false diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index ed92440ef..d52039221 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -89,6 +89,7 @@ @integer/config_long_press_key_timeout @integer/config_long_press_shift_key_timeout @integer/config_ignore_alt_code_key_timeout + @integer/config_disable_gesture_while_fast_typing_timeout @bool/config_show_more_keys_keyboard_at_touched_point @integer/config_language_on_spacebar_final_alpha @anim/language_on_spacebar_fadeout diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 4ed0f58e1..f5c1b7a0c 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -142,12 +142,14 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key private static final int MSG_REPEAT_KEY = 1; private static final int MSG_LONGPRESS_KEY = 2; private static final int MSG_DOUBLE_TAP = 3; + private static final int MSG_DISABLE_GESTURE_EXPIRED = 4; private final int mKeyRepeatStartTimeout; private final int mKeyRepeatInterval; private final int mLongPressKeyTimeout; private final int mLongPressShiftKeyTimeout; private final int mIgnoreAltCodeKeyTimeout; + private final int mDisableGestureWhileFastTypingTimeout; public KeyTimerHandler(final MainKeyboardView outerInstance, final TypedArray mainKeyboardViewAttr) { @@ -163,6 +165,8 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0); mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); + mDisableGestureWhileFastTypingTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_disableGestureWhileFastTypingTimeout, 0); } @Override @@ -187,6 +191,9 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1); } break; + case MSG_DISABLE_GESTURE_EXPIRED: + PointerTracker.clearGestureOffWhileFastTyping(); + break; } } @@ -311,6 +318,14 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key return hasMessages(MSG_TYPING_STATE_EXPIRED); } + @Override + public void startGestureOffWhileFastTypingTimer() { + removeMessages(MSG_DISABLE_GESTURE_EXPIRED); + PointerTracker.setGestureOffWhileFastTyping(); + sendMessageDelayed(obtainMessage(MSG_DISABLE_GESTURE_EXPIRED), + mDisableGestureWhileFastTypingTimeout); + } + @Override public void startDoubleTapTimer() { sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP), diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d4902ec28..89b6e7bc5 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -46,6 +46,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static boolean sMainDictionaryAvailable = false; private static boolean sGestureHandlingEnabledByInputField = false; private static boolean sGestureHandlingEnabledByUser = false; + private static boolean sGestureOffWhileFastTyping = false; public interface KeyEventHandler { /** @@ -84,6 +85,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { public interface TimerProxy { public void startTypingStateTimer(Key typedKey); public boolean isTypingState(); + public void startGestureOffWhileFastTypingTimer(); public void startKeyRepeatTimer(PointerTracker tracker); public void startLongPressTimer(PointerTracker tracker); public void startLongPressTimer(int code); @@ -99,6 +101,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { @Override public boolean isTypingState() { return false; } @Override + public void startGestureOffWhileFastTypingTimer() {} + @Override public void startKeyRepeatTimer(PointerTracker tracker) {} @Override public void startLongPressTimer(PointerTracker tracker) {} @@ -225,6 +229,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static void updateGestureHandlingMode() { sShouldHandleGesture = sMainDictionaryAvailable + && !sGestureOffWhileFastTyping && sGestureHandlingEnabledByInputField && sGestureHandlingEnabledByUser && !AccessibilityUtils.getInstance().isTouchExplorationEnabled(); @@ -241,6 +246,16 @@ public class PointerTracker implements PointerTrackerQueue.Element { updateGestureHandlingMode(); } + public static void setGestureOffWhileFastTyping() { + sGestureOffWhileFastTyping = true; + updateGestureHandlingMode(); + } + + public static void clearGestureOffWhileFastTyping() { + sGestureOffWhileFastTyping = false; + updateGestureHandlingMode(); + } + public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) { final ArrayList trackers = sTrackers; @@ -346,8 +361,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (key.isEnabled() || altersCode) { if (code == Keyboard.CODE_OUTPUT_TEXT) { mListener.onTextInput(key.getOutputText()); + mTimerProxy.startGestureOffWhileFastTypingTimer(); } else if (code != Keyboard.CODE_UNSPECIFIED) { mListener.onCodeInput(code, x, y); + mTimerProxy.startGestureOffWhileFastTypingTimer(); } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 73413f698..193c3a4ba 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -45,7 +45,7 @@ public class GestureStroke { private int mDetectFastMoveY; // TODO: Move some of these to resource. - private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.75f; + private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.60f; private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f; -- cgit v1.2.3-83-g751a From 82d9deaaf252cd20f8918adbc7a4b9b8f2647c38 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Fri, 21 Sep 2012 21:21:58 +0900 Subject: Combine mHasParentAddress with mHasLinkedListNode into mSupportsDynamicUpdate. bug: 6669677 Change-Id: I82799af199358420f09ac34fc005091e202c5d3b --- .../latin/makedict/BinaryDictIOUtils.java | 2 +- .../latin/makedict/BinaryDictInputOutput.java | 35 +++++++++---------- .../inputmethod/latin/makedict/FormatSpec.java | 33 +++++------------- .../latin/makedict/BinaryDictIOTests.java | 40 +++++++++------------- 4 files changed, 42 insertions(+), 68 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 397532933..ac0fb0ece 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -92,7 +92,7 @@ public class BinaryDictIOUtils { } if (p.mPosition == p.mNumOfCharGroup) { - if (formatOptions.mHasLinkedListNode) { + if (formatOptions.mSupportsDynamicUpdate) { final int forwardLinkAddress = buffer.readUnsignedInt24(); if (forwardLinkAddress != FormatSpec.NO_FORWARD_LINK_ADDRESS) { // the node has a forward link. diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 7b8dc5cc5..4806bf9dc 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -376,7 +376,7 @@ public class BinaryDictInputOutput { g.mCachedSize = groupSize; size += groupSize; } - if (options.mHasLinkedListNode) { + if (options.mSupportsDynamicUpdate) { size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE; } node.mCachedSize = size; @@ -390,11 +390,11 @@ public class BinaryDictInputOutput { } /** - * Helper method to check whether the CharGroup has a parent address. + * Helper method to check whether the dictionary can be updated dynamically. */ - public static boolean hasParentAddress(final FormatOptions options) { - return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_PARENT_ADDRESS - && options.mHasParentAddress; + public static boolean supportsDynamicUpdate(final FormatOptions options) { + return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE + && options.mSupportsDynamicUpdate; } /** @@ -404,7 +404,7 @@ public class BinaryDictInputOutput { * @param options file format options. */ private static int getGroupHeaderSize(final CharGroup group, final FormatOptions options) { - if (hasParentAddress(options)) { + if (supportsDynamicUpdate(options)) { return FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE + getGroupCharactersSize(group); } else { @@ -530,7 +530,7 @@ public class BinaryDictInputOutput { group.mCachedSize = groupSize; size += groupSize; } - if (formatOptions.mHasLinkedListNode) { + if (formatOptions.mSupportsDynamicUpdate) { size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE; } if (node.mCachedSize != size) { @@ -559,7 +559,8 @@ public class BinaryDictInputOutput { groupOffset += g.mCachedSize; } final int nodeSize = groupCountSize + groupOffset - + (formatOptions.mHasLinkedListNode ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0); + + (formatOptions.mSupportsDynamicUpdate + ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0); if (nodeSize != n.mCachedSize) { throw new RuntimeException("Bug : Stored and computed node size differ"); } @@ -792,8 +793,7 @@ public class BinaryDictInputOutput { return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0) + (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0) + (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0) - + (formatOptions.mHasParentAddress ? FormatSpec.HAS_PARENT_ADDRESS : 0) - + (formatOptions.mHasLinkedListNode ? FormatSpec.HAS_LINKEDLIST_NODE : 0); + + (formatOptions.mSupportsDynamicUpdate ? FormatSpec.SUPPORTS_DYNAMIC_UPDATE : 0); } /** @@ -857,7 +857,7 @@ public class BinaryDictInputOutput { byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset); buffer[index++] = flags; - if (hasParentAddress(formatOptions)) { + if (supportsDynamicUpdate(formatOptions)) { if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) { // this node is the root node. buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; @@ -927,7 +927,7 @@ public class BinaryDictInputOutput { } } - if (formatOptions.mHasLinkedListNode) { + if (formatOptions.mSupportsDynamicUpdate) { buffer[index] = buffer[index + 1] = buffer[index + 2] = FormatSpec.NO_FORWARD_LINK_ADDRESS; index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE; @@ -1112,7 +1112,7 @@ public class BinaryDictInputOutput { ++addressPointer; final int parentAddress; - if (hasParentAddress(options)) { + if (supportsDynamicUpdate(options)) { // read the parent address. (version 3) parentAddress = -buffer.readUnsignedInt24(); addressPointer += 3; @@ -1251,7 +1251,7 @@ public class BinaryDictInputOutput { final String result; final int originalPointer = buffer.position(); - if (hasParentAddress(formatOptions)) { + if (supportsDynamicUpdate(formatOptions)) { result = getWordAtAddressWithParentAddress(buffer, headerSize, address, formatOptions); } else { result = getWordAtAddressWithoutParentAddress(buffer, headerSize, address, @@ -1392,7 +1392,7 @@ public class BinaryDictInputOutput { } // reach the end of the array. - if (options.mHasLinkedListNode) { + if (options.mSupportsDynamicUpdate) { final int nextAddress = buffer.readUnsignedInt24(); if (nextAddress >= 0 && nextAddress < buffer.limit()) { buffer.position(nextAddress); @@ -1400,7 +1400,7 @@ public class BinaryDictInputOutput { break; } } - } while (options.mHasLinkedListNode && + } while (options.mSupportsDynamicUpdate && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS); final Node node = new Node(nodeContents); @@ -1469,8 +1469,7 @@ public class BinaryDictInputOutput { 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), new FormatOptions(version, - 0 != (optionsFlags & FormatSpec.HAS_PARENT_ADDRESS), - 0 != (optionsFlags & FormatSpec.HAS_LINKEDLIST_NODE))); + 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); return header; } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index adc6037bb..63a61b46f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -145,17 +145,14 @@ public final class FormatSpec { static final int MAXIMUM_SUPPORTED_VERSION = 3; static final int NOT_A_VERSION_NUMBER = -1; static final int FIRST_VERSION_WITH_HEADER_SIZE = 2; - static final int FIRST_VERSION_WITH_PARENT_ADDRESS = 3; - static final int FIRST_VERSION_WITH_LINKEDLIST_NODE = 3; + static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3; // These options need to be the same numeric values as the one in the native reading code. static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; // TODO: Make the native reading code read this variable. - static final int HAS_PARENT_ADDRESS = 0x2; + static final int SUPPORTS_DYNAMIC_UPDATE = 0x2; static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4; static final int CONTAINS_BIGRAMS_FLAG = 0x8; - // TODO: Make the native reading code read this variable. - static final int HAS_LINKEDLIST_NODE = 0x10; // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. @@ -215,31 +212,17 @@ public final class FormatSpec { */ public static class FormatOptions { public final int mVersion; - public final boolean mHasParentAddress; - public final boolean mHasLinkedListNode; + public final boolean mSupportsDynamicUpdate; public FormatOptions(final int version) { this(version, false); } - public FormatOptions(final int version, final boolean hasParentAddress) { - this(version, hasParentAddress, false); - } - public FormatOptions(final int version, final boolean hasParentAddress, - final boolean hasLinkedListNode) { + public FormatOptions(final int version, final boolean supportsDynamicUpdate) { mVersion = version; - if (version < FIRST_VERSION_WITH_PARENT_ADDRESS && hasParentAddress) { - throw new RuntimeException("Parent addresses are only supported with versions " - + FIRST_VERSION_WITH_PARENT_ADDRESS + " and ulterior."); - } - mHasParentAddress = hasParentAddress; - - if (version < FIRST_VERSION_WITH_LINKEDLIST_NODE && hasLinkedListNode) { - throw new RuntimeException("Linked list nodes are only supported with versions " - + FIRST_VERSION_WITH_LINKEDLIST_NODE + " and ulterior."); - } - if (!hasParentAddress && hasLinkedListNode) { - throw new RuntimeException("Linked list nodes need parent addresses."); + if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) { + throw new RuntimeException("Dynamic updates are only supported with versions " + + FIRST_VERSION_WITH_DYNAMIC_UPDATE + " and ulterior."); } - mHasLinkedListNode = hasLinkedListNode; + mSupportsDynamicUpdate = supportsDynamicUpdate; } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java index 539021f24..2f954318c 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java @@ -65,13 +65,10 @@ public class BinaryDictIOTests extends AndroidTestCase { CollectionUtils.newSparseArray(); private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2); - private static final FormatSpec.FormatOptions VERSION3_WITHOUT_PARENTADDRESS = - new FormatSpec.FormatOptions(3, false /* hasParentAddress */); - private static final FormatSpec.FormatOptions VERSION3_WITH_PARENTADDRESS = - new FormatSpec.FormatOptions(3, true /* hasParentAddress */); - private static final FormatSpec.FormatOptions VERSION3_WITH_LINKEDLIST_NODE = - new FormatSpec.FormatOptions(3, true /* hasParentAddress */, - true /* hasLinkedListNode */); + private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */); private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", @@ -240,8 +237,7 @@ public class BinaryDictIOTests extends AndroidTestCase { String result = " : buffer type = " + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); result += " : version = " + formatOptions.mVersion; - return result + ", hasParentAddress = " + formatOptions.mHasParentAddress - + ", hasLinkedListNode = " + formatOptions.mHasLinkedListNode; + return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate; } // Tests for readDictionaryBinary and writeDictionaryBinary @@ -308,9 +304,8 @@ public class BinaryDictIOTests extends AndroidTestCase { final List results = CollectionUtils.newArrayList(); runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_LINKEDLIST_NODE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -321,9 +316,8 @@ public class BinaryDictIOTests extends AndroidTestCase { final List results = CollectionUtils.newArrayList(); runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_LINKEDLIST_NODE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -455,9 +449,8 @@ public class BinaryDictIOTests extends AndroidTestCase { final List results = CollectionUtils.newArrayList(); runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_LINKEDLIST_NODE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -468,9 +461,8 @@ public class BinaryDictIOTests extends AndroidTestCase { final List results = CollectionUtils.newArrayList(); runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_LINKEDLIST_NODE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -528,7 +520,7 @@ public class BinaryDictIOTests extends AndroidTestCase { new FusionDictionary.DictionaryOptions( new HashMap(), false, false)); addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - timeWritingDictToFile(file, dict, VERSION3_WITH_LINKEDLIST_NODE); + timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY); @@ -569,7 +561,7 @@ public class BinaryDictIOTests extends AndroidTestCase { public void testDeleteWord() { File file = null; try { - file = File.createTempFile("testGetTerminalPosition", ".dict"); + file = File.createTempFile("testDeleteWord", ".dict"); } catch (IOException e) { // do nothing } @@ -579,7 +571,7 @@ public class BinaryDictIOTests extends AndroidTestCase { new FusionDictionary.DictionaryOptions( new HashMap(), false, false)); addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - timeWritingDictToFile(file, dict, VERSION3_WITH_LINKEDLIST_NODE); + timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY); -- cgit v1.2.3-83-g751a From fff8613df04af17bf56db866075b220d1ef0fbe8 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 24 Sep 2012 14:40:23 +0900 Subject: Fix a race condition with shift Bug: 7062092 Change-Id: Ie1984673e9ffc9291d9650ec38ce99031ad93585 --- java/src/com/android/inputmethod/latin/LatinIME.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 9252b0980..b77eef412 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -856,7 +856,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: the following is probably better done in resetEntireInputState(). // it should only happen when the cursor moved, and the very purpose of the // test below is to narrow down whether this happened or not. Likewise with - // the call to postUpdateShiftState. + // the call to updateShiftState. // We set this to NONE because after a cursor move, we don't want the space // state-related special processing to kick in. mSpaceState = SPACE_STATE_NONE; @@ -865,7 +865,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetEntireInputState(newSelStart); } - mHandler.postUpdateShiftState(); + mKeyboardSwitcher.updateShiftState(); } mExpectingUpdateSelection = false; // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not @@ -1551,7 +1551,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace(final int spaceState) { - // In many cases, we may have to put the keyboard in auto-shift state again. + // In many cases, we may have to put the keyboard in auto-shift state again. However + // we want to wait a few milliseconds before doing it to avoid the keyboard flashing + // during key repeat. mHandler.postUpdateShiftState(); if (mWordComposer.isComposingWord()) { @@ -1791,7 +1793,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)primaryCode, x, y); } - mHandler.postUpdateShiftState(); + mKeyboardSwitcher.updateShiftState(); return didAutoCorrect; } -- cgit v1.2.3-83-g751a From b305e6775a214f1cc16e584484e26a47eb8baa52 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 14 Sep 2012 14:00:51 +0900 Subject: Second finger can start gesture input Bug: 7108075 Change-Id: I17c419086a86c0bdac7a3858f8c66f6893678e67 --- .../com/android/inputmethod/keyboard/PointerTracker.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 89b6e7bc5..0778ad902 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -685,18 +685,13 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (!sShouldHandleGesture) { return; } - final int activePointerTrackerCount = getActivePointerTrackerCount(); - if (activePointerTrackerCount == 1) { - mIsDetectingGesture = false; - // A gesture should start only from the letter key. - final boolean isAlphabetKeyboard = (mKeyboard != null) - && mKeyboard.mId.isAlphabetKeyboard(); - if (isAlphabetKeyboard && !mIsShowingMoreKeysPanel && key != null - && Keyboard.isLetterCode(key.mCode)) { + // A gesture should start only from the letter key. + mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard() + && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode); + if (mIsDetectingGesture) { + if (getActivePointerTrackerCount() == 1) { sGestureFirstDownTime = eventTime; - onGestureDownEvent(x, y, eventTime); } - } else if (sInGesture && activePointerTrackerCount > 1) { onGestureDownEvent(x, y, eventTime); } } -- cgit v1.2.3-83-g751a From 6efe788494195fb8a57a2317cfa35e01ae7fda13 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 24 Sep 2012 17:27:03 +0900 Subject: Set punctuation suggestions at start if activated Bug: 7218468 Change-Id: I9f8c1f13dc26cedb95b65279a34a825021dfa72a --- java/src/com/android/inputmethod/latin/LatinIME.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b77eef412..45ecfd2e7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -715,7 +715,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSpaceState = SPACE_STATE_NONE; if (mSuggestionStripView != null) { - mSuggestionStripView.clear(); + // This will set the punctuation suggestions if next word suggestion is off; + // otherwise it will clear the suggestion strip. + setPunctuationSuggestions(); } } -- cgit v1.2.3-83-g751a From 72c5d328882976a0b4ae8b01a872ff5ae4d10547 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 24 Sep 2012 19:33:42 +0900 Subject: Fix a bug where the cursor pos wouldn't be tracked correctly This would cause Bug: 7208199 Change-Id: I8474985bfe349e1530b27bc98842937627bbe4e0 --- java/src/com/android/inputmethod/latin/LatinIME.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b77eef412..00608c5c5 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -719,7 +719,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - mConnection.resetCachesUponCursorMove(mLastSelectionStart); + mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart); if (isDifferentTextField) { mainKeyboardView.closing(); -- cgit v1.2.3-83-g751a From 6a58bb7ac95e804f87c4e88b5eb970d28210518e Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 24 Sep 2012 16:15:20 +0900 Subject: Add suggestion span upon starting a gesture. Bug: 7196761 Change-Id: If60cb809b205159ced1887f94b7caf9775fcf3bb --- java/src/com/android/inputmethod/latin/LatinIME.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b77eef412..5712d3b05 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -862,6 +862,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSpaceState = SPACE_STATE_NONE; if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) { + // If we are composing a word and moving the cursor, we would want to set a + // suggestion span for recorrection to work correctly. Unfortunately, that + // would involve the keyboard committing some new text, which would move the + // cursor back to where it was. Latin IME could then fix the position of the cursor + // again, but the asynchronous nature of the calls results in this wreaking havoc + // with selection on double tap and the like. + // Another option would be to send suggestions each time we set the composing + // text, but that is probably too expensive to do, so we decided to leave things + // as is. resetEntireInputState(newSelStart); } @@ -1087,11 +1096,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!mWordComposer.isComposingWord()) return; final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { - mConnection.commitText(typedWord, 1); - final CharSequence prevWord = addToUserHistoryDictionary(typedWord); - mLastComposedWord = mWordComposer.commitWord( - LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), - separatorString, prevWord); + commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, + separatorString); } } -- cgit v1.2.3-83-g751a From 8ec0064c49e80945dbe1bb31129eb890478b7e06 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Thu, 20 Sep 2012 16:21:40 +0900 Subject: Make children addresses and parent addresses use signed addresses. Signed addresses are used only in version 3 with dynamic update. bug: 6669677 Change-Id: Iadaeab199b5019d2330b4573c24da74d64f0945e --- .../latin/makedict/BinaryDictInputOutput.java | 212 +++++++++++++++------ .../inputmethod/latin/makedict/FormatSpec.java | 21 +- 2 files changed, 166 insertions(+), 67 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 4806bf9dc..d4a4d7cda 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -36,7 +36,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Stack; import java.util.TreeMap; /** @@ -412,6 +411,10 @@ public class BinaryDictInputOutput { } } + private static final int UINT8_MAX = 0xFF; + private static final int UINT16_MAX = 0xFFFF; + private static final int UINT24_MAX = 0xFFFFFF; + /** * Compute the size, in bytes, that an address will occupy. * @@ -423,17 +426,25 @@ public class BinaryDictInputOutput { * @return the byte size. */ private static int getByteSize(final int address) { - assert(address < 0x1000000); + assert(address <= UINT24_MAX); if (!hasChildrenAddress(address)) { return 0; - } else if (Math.abs(address) < 0x100) { + } else if (Math.abs(address) <= UINT8_MAX) { return 1; - } else if (Math.abs(address) < 0x10000) { + } else if (Math.abs(address) <= UINT16_MAX) { return 2; } else { return 3; } } + + private static final int SINT8_MAX = 0x7F; + private static final int SINT16_MAX = 0x7FFF; + private static final int SINT24_MAX = 0x7FFFFF; + private static final int MSB8 = 0x80; + private static final int MSB16 = 0x8000; + private static final int MSB24 = 0x800000; + // End utility methods. // This method is responsible for finding a nice ordering of the nodes that favors run-time @@ -509,13 +520,19 @@ public class BinaryDictInputOutput { } int groupSize = getGroupHeaderSize(group, formatOptions); if (group.isTerminal()) groupSize += FormatSpec.GROUP_FREQUENCY_SIZE; - if (null != group.mChildren) { + if (null == group.mChildren && formatOptions.mSupportsDynamicUpdate) { + groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + } else if (null != group.mChildren) { final int offsetBasePoint = groupSize + node.mCachedAddress + size; final int offset = group.mChildren.mCachedAddress - offsetBasePoint; // assign my address to children's parent address group.mChildren.mCachedParentAddress = group.mCachedAddress - group.mChildren.mCachedAddress; - groupSize += getByteSize(offset); + if (formatOptions.mSupportsDynamicUpdate) { + groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + } else { + groupSize += getByteSize(offset); + } } groupSize += getShortcutListSize(group.mShortcutTargets); if (null != group.mBigrams) { @@ -669,27 +686,52 @@ public class BinaryDictInputOutput { } } + /** + * Helper method to write a variable-size signed address to a file. + * + * @param buffer the buffer to write to. + * @param index the index in the buffer to write the address to. + * @param address the address to write. + * @return the size in bytes the address actually took. + */ + private static int writeVariableSignedAddress(final byte[] buffer, int index, + final int address) { + if (!hasChildrenAddress(address)) { + buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; + } else { + final int absAddress = Math.abs(address); + buffer[index++] = (byte)((address < 0 ? MSB8 : 0) | (0xFF & (absAddress >> 16))); + buffer[index++] = (byte)(0xFF & (absAddress >> 8)); + buffer[index++] = (byte)(0xFF & absAddress); + } + return 3; + } + private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress, - final int childrenOffset) { + final int childrenOffset, final FormatOptions formatOptions) { byte flags = 0; if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS; if (group.mFrequency >= 0) { flags |= FormatSpec.FLAG_IS_TERMINAL; } if (null != group.mChildren) { - switch (getByteSize(childrenOffset)) { - case 1: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; - break; - case 2: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; - break; - case 3: - flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; - break; - default: - throw new RuntimeException("Node with a strange address"); - } + final int byteSize = formatOptions.mSupportsDynamicUpdate + ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset); + switch (byteSize) { + case 1: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE; + break; + case 2: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES; + break; + case 3: + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; + break; + default: + throw new RuntimeException("Node with a strange address"); + } + } else if (formatOptions.mSupportsDynamicUpdate) { + flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES; } if (null != group.mShortcutTargets) { if (DBG && 0 == group.mShortcutTargets.size()) { @@ -808,6 +850,25 @@ public class BinaryDictInputOutput { + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY); } + private static final int writeParentAddress(final byte[] buffer, final int index, + final int address, final FormatOptions formatOptions) { + if (supportsDynamicUpdate(formatOptions)) { + if (address == FormatSpec.NO_PARENT_ADDRESS) { + buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; + } else { + final int absAddress = Math.abs(address); + assert(absAddress <= SINT24_MAX); + buffer[index] = (byte)((address < 0 ? MSB8 : 0) + | ((absAddress >> 16) & 0xFF)); + buffer[index + 1] = (byte)((absAddress >> 8) & 0xFF); + buffer[index + 2] = (byte)(absAddress & 0xFF); + } + return index + 3; + } else { + return index; + } + } + /** * Write a node to memory. The node is expected to have its final position cached. * @@ -854,22 +915,15 @@ public class BinaryDictInputOutput { final int childrenOffset = null == group.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS : group.mChildren.mCachedAddress - groupAddress; - byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset); + byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset, formatOptions); buffer[index++] = flags; - if (supportsDynamicUpdate(formatOptions)) { - if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) { - // this node is the root node. - buffer[index] = buffer[index + 1] = buffer[index + 2] = 0; - } else { - // write parent address. (version 3) - final int actualParentAddress = Math.abs(parentAddress - + (node.mCachedAddress - group.mCachedAddress)); - buffer[index] = (byte)((actualParentAddress >> 16) & 0xFF); - buffer[index + 1] = (byte)((actualParentAddress >> 8) & 0xFF); - buffer[index + 2] = (byte)(actualParentAddress & 0xFF); - } - index += 3; + if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) { + index = writeParentAddress(buffer, index, parentAddress, formatOptions); + } else { + index = writeParentAddress(buffer, index, + parentAddress + (node.mCachedAddress - group.mCachedAddress), + formatOptions); } index = CharEncoding.writeCharArray(group.mChars, buffer, index); @@ -879,7 +933,13 @@ public class BinaryDictInputOutput { if (group.mFrequency >= 0) { buffer[index++] = (byte) group.mFrequency; } - final int shift = writeVariableAddress(buffer, index, childrenOffset); + + final int shift; + if (formatOptions.mSupportsDynamicUpdate) { + shift = writeVariableSignedAddress(buffer, index, childrenOffset); + } else { + shift = writeVariableAddress(buffer, index, childrenOffset); + } index += shift; groupAddress += shift; @@ -1104,6 +1164,58 @@ public class BinaryDictInputOutput { // Input methods: Read a binary dictionary to memory. // readDictionaryBinary is the public entry point for them. + private static int getChildrenAddressSize(final int optionFlags, + final FormatOptions formatOptions) { + if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) { + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: + return 1; + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: + return 2; + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: + return 3; + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS: + default: + return 0; + } + } + + private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer, + final int optionFlags, final FormatOptions options) { + if (options.mSupportsDynamicUpdate) { + final int address = buffer.readUnsignedInt24(); + if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; + if ((address & MSB24) != 0) { + return -(address & SINT24_MAX); + } else { + return address; + } + } + int address; + switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) { + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: + return buffer.readUnsignedByte(); + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: + return buffer.readUnsignedShort(); + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: + return buffer.readUnsignedInt24(); + case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS: + default: + return FormatSpec.NO_CHILDREN_ADDRESS; + } + } + + private static int readParentAddress(final FusionDictionaryBufferInterface buffer, + final FormatOptions formatOptions) { + if (supportsDynamicUpdate(formatOptions)) { + final int parentAddress = buffer.readUnsignedInt24(); + final int sign = ((parentAddress & MSB24) != 0) ? -1 : 1; + return sign * (parentAddress & SINT24_MAX); + } else { + return FormatSpec.NO_PARENT_ADDRESS; + } + } + private static final int[] CHARACTER_BUFFER = new int[FormatSpec.MAX_WORD_LENGTH]; public static CharGroupInfo readCharGroup(final FusionDictionaryBufferInterface buffer, final int originalGroupAddress, final FormatOptions options) { @@ -1111,13 +1223,9 @@ public class BinaryDictInputOutput { final int flags = buffer.readUnsignedByte(); ++addressPointer; - final int parentAddress; + final int parentAddress = readParentAddress(buffer, options); if (supportsDynamicUpdate(options)) { - // read the parent address. (version 3) - parentAddress = -buffer.readUnsignedInt24(); addressPointer += 3; - } else { - parentAddress = FormatSpec.NO_PARENT_ADDRESS; } final int characters[]; @@ -1146,25 +1254,11 @@ public class BinaryDictInputOutput { } else { frequency = CharGroup.NOT_A_TERMINAL; } - int childrenAddress = addressPointer; - switch (flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) { - case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: - childrenAddress += buffer.readUnsignedByte(); - addressPointer += 1; - break; - case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: - childrenAddress += buffer.readUnsignedShort(); - addressPointer += 2; - break; - case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: - childrenAddress += buffer.readUnsignedInt24(); - addressPointer += 3; - break; - case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS: - default: - childrenAddress = FormatSpec.NO_CHILDREN_ADDRESS; - break; + int childrenAddress = readChildrenAddress(buffer, flags, options); + if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { + childrenAddress += addressPointer; } + addressPointer += getChildrenAddressSize(flags, options); ArrayList shortcutTargets = null; if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) { final int pointerBefore = buffer.position(); @@ -1250,6 +1344,7 @@ public class BinaryDictInputOutput { final String result; final int originalPointer = buffer.position(); + buffer.position(address); if (supportsDynamicUpdate(formatOptions)) { result = getWordAtAddressWithParentAddress(buffer, headerSize, address, formatOptions); @@ -1279,7 +1374,6 @@ public class BinaryDictInputOutput { sGetWordBuffer[index--] = currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1]; } - if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break; currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress; } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 63a61b46f..cab0661f6 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -42,11 +42,13 @@ public final class FormatSpec { * ps * * f | - * o | IF HAS_LINKEDLIST_NODE (defined in the file header) + * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header) * r | forward link address, 3byte - * w | the address must be positive. - * a | - * rdlinkaddress + * w | 1 byte = bbbbbbbb match + * a | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte) + * r | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte + * d | + * linkaddress */ /* Node(CharGroup) layout is as follows: @@ -63,11 +65,13 @@ public final class FormatSpec { * | is blacklisted ? 1 bit, 1 = yes, 0 = no : FLAG_IS_BLACKLISTED * * p | - * a | IF HAS_PARENT_ADDRESS (defined in the file header) + * a | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header) * r | parent address, 3byte - * e | the address must be negative, so the absolute value of the address is stored. - * n | - * taddress + * e | 1 byte = bbbbbbbb match + * n | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte) + * t | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte + * a | + * ddress * * c | IF FLAG_HAS_MULTIPLE_CHARS * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers @@ -206,6 +210,7 @@ public final class FormatSpec { // This option needs to be the same numeric value as the one in binary_format.h. static final int NOT_VALID_WORD = -99; + static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3; /** * Options about file format. -- cgit v1.2.3-83-g751a From 6ca50d99208efdbcad96b3260fe7592bf95a6b00 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 25 Sep 2012 11:58:36 +0900 Subject: Fix touch position correction data passing Bug: 7225811 Change-Id: I3dcc0385ed633585d6810fdb742e0f88c2efbbd2 --- .../inputmethod/keyboard/KeyboardLayoutSet.java | 11 +++++--- .../inputmethod/keyboard/ProximityInfo.java | 16 +++++------ .../keyboard/internal/KeyboardBuilder.java | 7 +++-- .../keyboard/internal/TouchPositionCorrection.java | 31 +++++++++++++++++----- 4 files changed, 41 insertions(+), 24 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index aaccf63ba..d97df7491 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -96,7 +96,7 @@ public class KeyboardLayoutSet { String mKeyboardLayoutSetName; int mMode; EditorInfo mEditorInfo; - boolean mTouchPositionCorrectionEnabled; + boolean mDisableTouchPositionCorrectionDataForTest; boolean mVoiceKeyEnabled; boolean mVoiceKeyOnMain; boolean mNoSettingsKey; @@ -167,7 +167,9 @@ public class KeyboardLayoutSet { } final int keyboardXmlId = elementParams.mKeyboardXmlId; builder.load(keyboardXmlId, id); - builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled); + if (mParams.mDisableTouchPositionCorrectionDataForTest) { + builder.disableTouchPositionCorrectionDataForTest(); + } builder.setProximityCharsCorrectionEnabled( elementParams.mProximityCharsCorrectionEnabled); keyboard = builder.build(); @@ -264,8 +266,9 @@ public class KeyboardLayoutSet { return this; } - public void setTouchPositionCorrectionEnabled(final boolean enabled) { - mParams.mTouchPositionCorrectionEnabled = enabled; + // For test only + public void disableTouchPositionCorrectionDataForTest() { + mParams.mDisableTouchPositionCorrectionDataForTest = true; } public KeyboardLayoutSet build() { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index e1b082c16..6e138946f 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -44,7 +44,6 @@ public class ProximityInfo { private final int mKeyboardHeight; private final int mMostCommonKeyWidth; private final Key[] mKeys; - private final TouchPositionCorrection mTouchPositionCorrection; private final Key[][] mGridNeighbors; private final String mLocaleStr; @@ -67,14 +66,13 @@ public class ProximityInfo { mKeyHeight = mostCommonKeyHeight; mMostCommonKeyWidth = mostCommonKeyWidth; mKeys = keys; - mTouchPositionCorrection = touchPositionCorrection; mGridNeighbors = new Key[mGridSize][]; if (minWidth == 0 || height == 0) { // No proximity required. Keyboard might be more keys keyboard. return; } computeNearestNeighbors(); - mNativeProximityInfo = createNativeProximityInfo(); + mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection); } public static ProximityInfo createDummyProximityInfo() { @@ -106,12 +104,12 @@ public class ProximityInfo { private native void releaseProximityInfoNative(long nativeProximityInfo); - private final long createNativeProximityInfo() { + private final long createNativeProximityInfo( + final TouchPositionCorrection touchPositionCorrection) { final Key[][] gridNeighborKeys = mGridNeighbors; final int keyboardWidth = mKeyboardMinWidth; final int keyboardHeight = mKeyboardHeight; final Key[] keys = mKeys; - final TouchPositionCorrection touchPositionCorrection = mTouchPositionCorrection; final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE); for (int i = 0; i < mGridSize; ++i) { @@ -148,12 +146,12 @@ public class ProximityInfo { final Key key = keys[i]; final Rect hitBox = key.mHitBox; final int row = hitBox.top / mKeyHeight; - if (row < touchPositionCorrection.mRadii.length) { + if (row < touchPositionCorrection.getRows()) { final int hitBoxWidth = hitBox.width(); final int hitBoxHeight = hitBox.height(); - final float x = touchPositionCorrection.mXs[row]; - final float y = touchPositionCorrection.mYs[row]; - final float radius = touchPositionCorrection.mRadii[row]; + final float x = touchPositionCorrection.getX(row); + final float y = touchPositionCorrection.getY(row); + final float radius = touchPositionCorrection.getRadius(row); sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth; sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight; // Note that, in recent versions of Android, FloatMath is actually slower than diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 31c7cb565..b314a3795 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -177,9 +177,9 @@ public class KeyboardBuilder { return this; } - // TODO: Remove this method. - public void setTouchPositionCorrectionEnabled(final boolean enabled) { - mParams.mTouchPositionCorrection.setEnabled(enabled); + // For test only + public void disableTouchPositionCorrectionDataForTest() { + mParams.mTouchPositionCorrection.setEnabled(false); } public void setProximityCharsCorrectionEnabled(final boolean enabled) { @@ -314,7 +314,6 @@ public class KeyboardBuilder { final int resourceId = keyboardAttr.getResourceId( R.styleable.Keyboard_touchPositionCorrectionData, 0); - params.mTouchPositionCorrection.setEnabled(resourceId != 0); if (resourceId != 0) { final String[] data = mResources.getStringArray(resourceId); params.mTouchPositionCorrection.load(data); diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java index 69dc01cd6..811a620b3 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java @@ -21,10 +21,10 @@ import com.android.inputmethod.latin.LatinImeLogger; public class TouchPositionCorrection { private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3; - public boolean mEnabled; - public float[] mXs; - public float[] mYs; - public float[] mRadii; + private boolean mEnabled; + private float[] mXs; + private float[] mYs; + private float[] mRadii; public void load(final String[] data) { final int dataLength = data.length; @@ -53,24 +53,41 @@ public class TouchPositionCorrection { mRadii[index] = value; } } + mEnabled = dataLength > 0; } catch (NumberFormatException e) { if (LatinImeLogger.sDBG) { throw new RuntimeException( "the number format for touch position correction data is invalid"); } + mEnabled = false; mXs = null; mYs = null; mRadii = null; } } - // TODO: Remove this method. + // For test only public void setEnabled(final boolean enabled) { mEnabled = enabled; } public boolean isValid() { - return mEnabled && mXs != null && mYs != null && mRadii != null - && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0; + return mEnabled; + } + + public int getRows() { + return mRadii.length; + } + + public float getX(final int row) { + return mXs[row]; + } + + public float getY(final int row) { + return mYs[row]; + } + + public float getRadius(final int row) { + return mRadii[row]; } } -- cgit v1.2.3-83-g751a From 630d9c95e596c902b80dcb57eb0386e94290406d Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 25 Sep 2012 14:23:23 +0900 Subject: Introduce typing aware gesture detection Bug: 7218902 Change-Id: I11ad85914bf991eca513e04ed8b5d12779101dda --- .../inputmethod/keyboard/MainKeyboardView.java | 9 +++-- .../inputmethod/keyboard/PointerTracker.java | 16 +++++--- .../keyboard/internal/GestureStroke.java | 47 +++++++++++++++++++--- 3 files changed, 57 insertions(+), 15 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index f5c1b7a0c..f1fcfe785 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -318,12 +318,13 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key return hasMessages(MSG_TYPING_STATE_EXPIRED); } + // TODO: Remove "gesture off while fast typing" related dead code. @Override public void startGestureOffWhileFastTypingTimer() { - removeMessages(MSG_DISABLE_GESTURE_EXPIRED); - PointerTracker.setGestureOffWhileFastTyping(); - sendMessageDelayed(obtainMessage(MSG_DISABLE_GESTURE_EXPIRED), - mDisableGestureWhileFastTypingTimeout); +// removeMessages(MSG_DISABLE_GESTURE_EXPIRED); +// PointerTracker.setGestureOffWhileFastTyping(); +// sendMessageDelayed(obtainMessage(MSG_DISABLE_GESTURE_EXPIRED), +// mDisableGestureWhileFastTypingTimeout); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 0778ad902..d6c567ef7 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -169,6 +169,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private boolean mIsDetectingGesture = false; // per PointerTracker. private static boolean sInGesture = false; private static long sGestureFirstDownTime; + private static long sLastLetterTypingUpTime; private static final InputPointers sAggregratedPointers = new InputPointers( GestureStroke.DEFAULT_CAPACITY); private static int sLastRecognitionPointSize = 0; @@ -698,6 +699,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private void onGestureDownEvent(final int x, final int y, final long eventTime) { mIsDetectingGesture = true; + mGestureStrokeWithPreviewPoints.setLastLetterTypingTime(eventTime, sLastLetterTypingUpTime); final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime); mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */); @@ -842,7 +844,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); } - onUpEventInternal(); + onUpEventInternal(eventTime); onDownEventInternal(x, y, eventTime); } else { // HACK: If there are currently multiple touches, register the key even if @@ -852,7 +854,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { // this hack. if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { - onUpEventInternal(); + onUpEventInternal(eventTime); } if (!mIsDetectingGesture) { mKeyAlreadyProcessed = true; @@ -897,7 +899,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } } - onUpEventInternal(); + onUpEventInternal(eventTime); if (queue != null) { queue.remove(this); } @@ -911,11 +913,11 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_EVENT) { printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime); } - onUpEventInternal(); + onUpEventInternal(eventTime); mKeyAlreadyProcessed = true; } - private void onUpEventInternal() { + private void onUpEventInternal(final long eventTime) { mTimerProxy.cancelKeyTimers(); mIsInSlidingKeyInput = false; mIsDetectingGesture = false; @@ -943,6 +945,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { } if (currentKey != null && !currentKey.isRepeatable()) { detectAndSendKey(currentKey, mKeyX, mKeyY); + final int code = currentKey.mCode; + if (Keyboard.isLetterCode(code) && code != Keyboard.CODE_SPACE) { + sLastLetterTypingUpTime = eventTime; + } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 193c3a4ba..9fe6fa3f8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -34,8 +34,10 @@ public class GestureStroke { private long mLastMajorEventTime; private int mLastMajorEventX; private int mLastMajorEventY; + private boolean mAfterFastTyping; private int mKeyWidth; + private int mStartGestureLengthThresholdAfterFastTyping; // pixel private int mStartGestureLengthThreshold; // pixel private int mMinGestureSamplingLength; // pixel private int mGestureRecognitionSpeedThreshold; // pixel / sec @@ -45,7 +47,11 @@ public class GestureStroke { private int mDetectFastMoveY; // TODO: Move some of these to resource. - private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.60f; + private static final int GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD = 350; // msec + private static final float START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH = + 8.0f; + private static final int START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION = 400; // msec + private static final float START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH = 0.6f; private static final int START_GESTURE_DURATION_THRESHOLD = 70; // msec private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f; @@ -67,6 +73,8 @@ public class GestureStroke { public void setKeyboardGeometry(final int keyWidth) { mKeyWidth = keyWidth; // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key? + mStartGestureLengthThresholdAfterFastTyping = (int)(keyWidth + * START_GESTURE_LENGTH_THRESHOLD_AFTER_FAST_TYPING_RATIO_TO_KEY_WIDTH); mStartGestureLengthThreshold = (int)(keyWidth * START_GESTURE_LENGTH_THRESHOLD_RATIO_TO_KEY_WIDTH); mMinGestureSamplingLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH); @@ -75,10 +83,33 @@ public class GestureStroke { mDetectFastMoveSpeedThreshold = (int)(keyWidth * DETECT_FAST_MOVE_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH); if (DEBUG) { - Log.d(TAG, "setKeyboardGeometry: keyWidth=" + keyWidth); + Log.d(TAG, "[" + mPointerId + "] setKeyboardGeometry: keyWidth=" + keyWidth + + " tL0=" + mStartGestureLengthThresholdAfterFastTyping + + " tL=" + mStartGestureLengthThreshold); } } + public void setLastLetterTypingTime(final long downTime, final long lastTypingTime) { + final long elpasedTimeAfterTyping = downTime - lastTypingTime; + if (elpasedTimeAfterTyping < GESTURE_AFTER_FAST_TYPING_DURATION_THRESHOLD) { + mAfterFastTyping = true; + } + if (DEBUG) { + Log.d(TAG, "[" + mPointerId + "] setLastTypingTime: dT=" + elpasedTimeAfterTyping + + " afterFastTyping=" + mAfterFastTyping); + } + } + + private int getStartGestureLengthThreshold(final int deltaTime) { + if (!mAfterFastTyping || deltaTime >= START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION) { + return mStartGestureLengthThreshold; + } + final int decayedThreshold = + (mStartGestureLengthThresholdAfterFastTyping - mStartGestureLengthThreshold) + * deltaTime / START_GESTURE_LENGTH_THRESHOLD_DECAY_DURATION; + return mStartGestureLengthThresholdAfterFastTyping - decayedThreshold; + } + public boolean isStartOfAGesture() { if (mDetectFastMoveTime == 0) { return false; @@ -92,10 +123,12 @@ public class GestureStroke { final int deltaLength = getDistance( mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex), mDetectFastMoveX, mDetectFastMoveY); + final int startGestureLengthThreshold = getStartGestureLengthThreshold(deltaTime); final boolean isStartOfAGesture = deltaTime > START_GESTURE_DURATION_THRESHOLD - && deltaLength > mStartGestureLengthThreshold; + && deltaLength > startGestureLengthThreshold; if (DEBUG) { - Log.d(TAG, "isStartOfAGesture: dT=" + deltaTime + " dL=" + deltaLength + Log.d(TAG, "[" + mPointerId + "] isStartOfAGesture: dT=" + deltaTime + + " dL=" + deltaLength + " tL=" + startGestureLengthThreshold + " points=" + size + (isStartOfAGesture ? " Detect start of a gesture" : "")); } return isStartOfAGesture; @@ -109,6 +142,7 @@ public class GestureStroke { mYCoordinates.setLength(0); mLastMajorEventTime = 0; mDetectFastMoveTime = 0; + mAfterFastTyping = false; } private void appendPoint(final int x, final int y, final int time) { @@ -135,12 +169,13 @@ public class GestureStroke { final int pixelsPerSec = pixels * MSEC_PER_SEC; if (DEBUG) { final float speed = (float)pixelsPerSec / msecs / mKeyWidth; - Log.d(TAG, String.format("Speed=%.3f keyWidth/sec", speed)); + Log.d(TAG, String.format("[" + mPointerId + "] speed=%.3f", speed)); } // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC) if (mDetectFastMoveTime == 0 && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) { if (DEBUG) { - Log.d(TAG, "Detect fast move: T=" + time + " points = " + size); + Log.d(TAG, "[" + mPointerId + "] detect fast move: T=" + + time + " points = " + size); } mDetectFastMoveTime = time; mDetectFastMoveX = x; -- cgit v1.2.3-83-g751a From 93d7c6233fb6b867d51a9eeb54b951fe3a377ea8 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Tue, 25 Sep 2012 16:14:16 +0900 Subject: Make getTerminalPosition read linked-list nodes. bug: 6669677 Change-Id: I599d276f430efe23d402695c325e23906b7705b3 --- .../latin/makedict/BinaryDictIOUtils.java | 80 +++++++++++++--------- 1 file changed, 48 insertions(+), 32 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index ac0fb0ece..19da5124a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -157,47 +157,63 @@ public class BinaryDictIOUtils { final int wordLen = word.codePointCount(0, word.length()); for (int depth = 0; depth < Constants.Dictionary.MAX_WORD_LENGTH; ++depth) { if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD; - int groupOffset = buffer.position() - header.mHeaderSize; - final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer); - groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount); - - for (int i = 0; i < charGroupCount; ++i) { - final int charGroupPos = buffer.position(); - final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, - buffer.position(), header.mFormatOptions); - boolean same = true; - for (int p = 0, j = word.offsetByCodePoints(0, wordPos); - p < currentInfo.mCharacters.length; - ++p, j = word.offsetByCodePoints(j, 1)) { - if (wordPos + p >= wordLen - || word.codePointAt(j) != currentInfo.mCharacters[p]) { - same = false; - break; + + do { + int groupOffset = buffer.position() - header.mHeaderSize; + final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer); + groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount); + + boolean foundNextCharGroup = false; + for (int i = 0; i < charGroupCount; ++i) { + final int charGroupPos = buffer.position(); + final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer, + buffer.position(), header.mFormatOptions); + boolean same = true; + for (int p = 0, j = word.offsetByCodePoints(0, wordPos); + p < currentInfo.mCharacters.length; + ++p, j = word.offsetByCodePoints(j, 1)) { + if (wordPos + p >= wordLen + || word.codePointAt(j) != currentInfo.mCharacters[p]) { + same = false; + break; + } } - } - if (same) { - if (wordPos + currentInfo.mCharacters.length == wordLen) { - if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) { + if (same) { + // found the group matches the word. + if (wordPos + currentInfo.mCharacters.length == wordLen) { + if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) { + return FormatSpec.NOT_VALID_WORD; + } else { + return charGroupPos; + } + } + wordPos += currentInfo.mCharacters.length; + if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) { return FormatSpec.NOT_VALID_WORD; - } else { - return charGroupPos; } + foundNextCharGroup = true; + buffer.position(currentInfo.mChildrenAddress); + break; } - wordPos += currentInfo.mCharacters.length; - if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) { - return FormatSpec.NOT_VALID_WORD; - } - buffer.position(currentInfo.mChildrenAddress); - break; + groupOffset = currentInfo.mEndAddress; } - groupOffset = currentInfo.mEndAddress; - // not found - if (i >= charGroupCount - 1) { + // If we found the next char group, it is under the file pointer. + // But if not, we are at the end of this node so we expect to have + // a forward link address that we need to consult and possibly resume + // search on the next node in the linked list. + if (foundNextCharGroup) break; + if (!header.mFormatOptions.mSupportsDynamicUpdate) { return FormatSpec.NOT_VALID_WORD; } - } + + final int forwardLinkAddress = buffer.readUnsignedInt24(); + if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) { + return FormatSpec.NOT_VALID_WORD; + } + buffer.position(forwardLinkAddress); + } while(true); } return FormatSpec.NOT_VALID_WORD; } -- cgit v1.2.3-83-g751a From a161bdac885fc8e5f0063d33b055b0a6ecdefbdb Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Tue, 25 Sep 2012 17:52:31 +0900 Subject: add capacity to FusionDictionaryBufferInterface. bug: 6669677 Change-Id: I4627093811a19c46ce13fe351d1db63cbd78cf4a --- java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java | 5 +++++ .../android/inputmethod/latin/makedict/BinaryDictInputOutput.java | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java index 4a3d11aa1..05255a6b3 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java @@ -100,6 +100,11 @@ public class UserHistoryDictIOUtils { @Override public int limit() { + return mBuffer.length - 1; + } + + @Override + public int capacity() { return mBuffer.length; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index d4a4d7cda..f9339de08 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -63,6 +63,7 @@ public class BinaryDictInputOutput { public void position(int newPosition); public void put(final byte b); public int limit(); + public int capacity(); } public static final class ByteBufferWrapper implements FusionDictionaryBufferInterface { @@ -112,6 +113,11 @@ public class BinaryDictInputOutput { public int limit() { return mBuffer.limit(); } + + @Override + public int capacity() { + return mBuffer.capacity(); + } } /** -- cgit v1.2.3-83-g751a From 936371e64fdbbbaa012526bd4d6df9e03682ab8d Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 26 Sep 2012 12:25:24 +0900 Subject: Don't cancel gesture trail update drawing Bug: 7216955 Change-Id: Ie12bf45637b1012c9addb47279f9653334fae702 --- .../android/inputmethod/keyboard/KeyboardView.java | 3 --- .../keyboard/internal/PreviewPlacerView.java | 20 ++------------------ 2 files changed, 2 insertions(+), 21 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index cf89567f8..d51e036b1 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) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 15170e040..b3fd284b1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -101,29 +101,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) { @@ -278,10 +266,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)) { -- cgit v1.2.3-83-g751a From 1645902cce7eaceff4aba3ea01d723240c6ce189 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 26 Sep 2012 12:46:55 +0900 Subject: Postpone gesture start detection on non-letter key Bug: 7108223 Change-Id: Ie1ead5cec947ddf86c5150dca5b20b224527e511 --- java/src/com/android/inputmethod/keyboard/PointerTracker.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d6c567ef7..cfd1e09f9 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"); } @@ -742,7 +745,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int gestureTime = (int)(eventTime - sGestureFirstDownTime); if (mIsDetectingGesture) { mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent); - mayStartBatchInput(); + mayStartBatchInput(key); if (sInGesture && key != null) { updateBatchInput(eventTime); } -- cgit v1.2.3-83-g751a From 2ee70804e92b17016a2f042c4f6b0e94b5d23e88 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Tue, 25 Sep 2012 20:48:25 +0900 Subject: Add moved char groups. bug: 6669677 Change-Id: I372f841044fe8e076a50a80ac10b715e5f8fd4eb --- .../latin/makedict/BinaryDictIOUtils.java | 11 +++++++++-- .../latin/makedict/BinaryDictInputOutput.java | 23 ++++++++++++++++++++-- .../inputmethod/latin/makedict/FormatSpec.java | 20 ++++++++++++------- 3 files changed, 43 insertions(+), 11 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 19da5124a..e2c1254ce 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; 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(); @@ -394,6 +395,13 @@ public class BinaryDictInputOutput { return FormatSpec.NO_CHILDREN_ADDRESS != address; } + /** + * 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. */ @@ -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 shortcutTargets = info.mShortcutTargets; ArrayList 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; -- cgit v1.2.3-83-g751a From 2aea34fb31f1a8a5fe24cccd1b9aab4908f2f8e2 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Wed, 26 Sep 2012 13:21:29 +0900 Subject: Add updateParentAddress. bug: 6669677 Change-Id: I353f8ae53720cdf7a809271a28cb703709609f53 --- .../latin/makedict/BinaryDictIOUtils.java | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index e2c1254ce..b97be0543 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -246,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); + } } -- cgit v1.2.3-83-g751a From 157fe98fd439a7d9cc063a7f5573f688e33c2f29 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 26 Sep 2012 15:49:23 +0900 Subject: Draw gesture trail that is above the keyboard Bug: 7233992 Change-Id: Ia848543a9d008c68d6ecbc7f715aa6ccdba9d1c6 --- .../android/inputmethod/keyboard/KeyboardView.java | 3 +- .../inputmethod/keyboard/PointerTracker.java | 28 +++--- .../keyboard/internal/PreviewPlacerView.java | 111 +++++++++++++++------ 3 files changed, 96 insertions(+), 46 deletions(-) (limited to 'java/src') 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); + } + } + + 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); } - canvas.translate(-mXOrigin, -mYOrigin); + 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) { -- cgit v1.2.3-83-g751a From 84d858ed5e187eb9d4b56b593e1d9287f762bbca Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Mon, 20 Aug 2012 19:29:20 +0900 Subject: Use BinaryDictInputOutput to save UserHistoryDictionary. bug: 6669677 Change-Id: I08193c26f76dbd48168f8ac02c1b737525bfc7b2 --- .../inputmethod/latin/UserHistoryDictIOUtils.java | 28 +- .../inputmethod/latin/UserHistoryDictionary.java | 482 ++++++--------------- .../latin/makedict/BinaryDictIOUtils.java | 5 +- .../latin/UserHistoryDictionaryTests.java | 109 +++++ 4 files changed, 270 insertions(+), 354 deletions(-) create mode 100644 tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java index 05255a6b3..519165dd0 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java @@ -115,11 +115,10 @@ public class UserHistoryDictIOUtils { public static void writeDictionaryBinary(final OutputStream destination, final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams, final FormatOptions formatOptions) { - final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams); - try { BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions); + Log.d(TAG, "end writing"); } catch (IOException e) { Log.e(TAG, "IO exception while writing file: " + e); } catch (UnsupportedFormatException e) { @@ -132,16 +131,18 @@ public class UserHistoryDictIOUtils { */ /* packages for test */ static FusionDictionary constructFusionDictionary( final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) { - final FusionDictionary fusionDict = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions( - new HashMap(), false, false)); - + new FusionDictionary.DictionaryOptions(new HashMap(), false, + false)); + int profTotal = 0; for (final String word1 : bigrams.keySet()) { final HashMap word1Bigrams = bigrams.getBigrams(word1); for (final String word2 : word1Bigrams.keySet()) { final int freq = dict.getFrequency(word1, word2); - + if (freq == -1) { + // don't add this bigram. + continue; + } if (DEBUG) { if (word1 == null) { Log.d(TAG, "add unigram: " + word2 + "," + Integer.toString(freq)); @@ -149,17 +150,22 @@ public class UserHistoryDictIOUtils { Log.d(TAG, "add bigram: " + word1 + "," + word2 + "," + Integer.toString(freq)); } + profTotal++; } - if (word1 == null) { // unigram fusionDict.add(word2, freq, null, false /* isNotAWord */); } else { // bigram + if (FusionDictionary.findWordInTree(fusionDict.mRoot, word1) == null) { + fusionDict.add(word1, 2, null, false /* isNotAWord */); + } fusionDict.setBigram(word1, word2, freq); } bigrams.updateBigram(word1, word2, (byte)freq); } } - + if (DEBUG) { + Log.d(TAG, "add " + profTotal + "words"); + } return fusionDict; } @@ -171,7 +177,6 @@ public class UserHistoryDictIOUtils { final Map unigrams = CollectionUtils.newTreeMap(); final Map frequencies = CollectionUtils.newTreeMap(); final Map> bigrams = CollectionUtils.newTreeMap(); - try { BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies, bigrams); @@ -189,14 +194,11 @@ public class UserHistoryDictIOUtils { /* package for test */ static void addWordsFromWordMap(final Map unigrams, final Map frequencies, final Map> bigrams, final OnAddWordListener to) { - for (Map.Entry entry : unigrams.entrySet()) { final String word1 = entry.getValue(); final int unigramFrequency = frequencies.get(entry.getKey()); to.setUnigram(word1, null, unigramFrequency); - final ArrayList attrList = bigrams.get(entry.getKey()); - if (attrList != null) { for (final PendingAttribute attr : attrList) { to.setBigram(word1, unigrams.get(attr.mAddress), diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index e03af649c..202aa642f 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -16,24 +16,25 @@ package com.android.inputmethod.latin; -import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; import android.os.AsyncTask; -import android.provider.BaseColumns; import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface; +import com.android.inputmethod.latin.UserHistoryDictIOUtils.OnAddWordListener; import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; @@ -43,45 +44,27 @@ import java.util.concurrent.locks.ReentrantLock; */ public class UserHistoryDictionary extends ExpandableDictionary { private static final String TAG = UserHistoryDictionary.class.getSimpleName(); + private static final String NAME = UserHistoryDictionary.class.getSimpleName(); public static final boolean DBG_SAVE_RESTORE = false; public static final boolean DBG_STRESS_TEST = false; public static final boolean DBG_ALWAYS_WRITE = false; public static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG; + private static final FormatOptions VERSION3 = new FormatOptions(3, + true /* supportsDynamicUpdate */); + /** Any pair being typed or picked */ private static final int FREQUENCY_FOR_TYPED = 2; /** Maximum number of pairs. Pruning will start when databases goes above this number. */ - public static final int sMaxHistoryBigrams = 10000; + public static final int MAX_HISTORY_BIGRAMS = 10000; /** * When it hits maximum bigram pair, it will delete until you are left with * only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs. * Do not keep this number small to avoid deleting too often. */ - public static final int sDeleteHistoryBigrams = 1000; - - /** - * Database version should increase if the database structure changes - */ - private static final int DATABASE_VERSION = 1; - - private static final String DATABASE_NAME = "userbigram_dict.db"; - - /** Name of the words table in the database */ - private static final String MAIN_TABLE_NAME = "main"; - // TODO: Consume less space by using a unique id for locale instead of the whole - // 2-5 character string. - private static final String MAIN_COLUMN_ID = BaseColumns._ID; - private static final String MAIN_COLUMN_WORD1 = "word1"; - private static final String MAIN_COLUMN_WORD2 = "word2"; - private static final String MAIN_COLUMN_LOCALE = "locale"; - - /** Name of the frequency table in the database */ - private static final String FREQ_TABLE_NAME = "frequency"; - private static final String FREQ_COLUMN_ID = BaseColumns._ID; - private static final String FREQ_COLUMN_PAIR_ID = "pair_id"; - private static final String COLUMN_FORGETTING_CURVE_VALUE = "freq"; + public static final int DELETE_HISTORY_BIGRAMS = 1000; /** Locale for which this user history dictionary is storing words */ private final String mLocale; @@ -91,29 +74,13 @@ public class UserHistoryDictionary extends ExpandableDictionary { private final ReentrantLock mBigramListLock = new ReentrantLock(); private final SharedPreferences mPrefs; - private final static HashMap sDictProjectionMap; - private final static ConcurrentHashMap> - sLangDictCache = CollectionUtils.newConcurrentHashMap(); - - static { - sDictProjectionMap = CollectionUtils.newHashMap(); - sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID); - sDictProjectionMap.put(MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD1); - sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2); - sDictProjectionMap.put(MAIN_COLUMN_LOCALE, MAIN_COLUMN_LOCALE); - - sDictProjectionMap.put(FREQ_COLUMN_ID, FREQ_COLUMN_ID); - sDictProjectionMap.put(FREQ_COLUMN_PAIR_ID, FREQ_COLUMN_PAIR_ID); - sDictProjectionMap.put(COLUMN_FORGETTING_CURVE_VALUE, COLUMN_FORGETTING_CURVE_VALUE); - } - - private static DatabaseHelper sOpenHelper = null; + // Should always be false except when we use this class for test + /* package for test */ boolean isTest = false; - public String getLocale() { - return mLocale; - } + private static final ConcurrentHashMap> + sLangDictCache = CollectionUtils.newConcurrentHashMap(); - public synchronized static UserHistoryDictionary getInstance( + public static synchronized UserHistoryDictionary getInstance( final Context context, final String locale, final SharedPreferences sp) { if (sLangDictCache.containsKey(locale)) { final SoftReference ref = sLangDictCache.get(locale); @@ -136,9 +103,6 @@ public class UserHistoryDictionary extends ExpandableDictionary { super(context, Dictionary.TYPE_USER_HISTORY); mLocale = locale; mPrefs = sp; - if (sOpenHelper == null) { - sOpenHelper = new DatabaseHelper(getContext()); - } if (mLocale != null && mLocale.length() > 1) { loadDictionary(); } @@ -190,6 +154,7 @@ public class UserHistoryDictionary extends ExpandableDictionary { try { super.addWord( word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED); + mBigramList.addBigram(null, word2, (byte)FREQUENCY_FOR_TYPED); // Do not insert a word as a bigram of itself if (word2.equals(word1)) { return 0; @@ -227,11 +192,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { * Schedules a background thread to write any pending words to the database. */ private void flushPendingWrites() { - if (mBigramListLock.isLocked()) { - return; - } // Create a background thread to write the pending entries - new UpdateDbTask(sOpenHelper, mBigramList, mLocale, this, mPrefs).execute(); + new UpdateBinaryTask(mBigramList, mLocale, this, mPrefs, getContext()).execute(); } @Override @@ -245,6 +207,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { } } + private int profTotal; + private void loadDictionaryAsyncLocked() { if (DBG_STRESS_TEST) { try { @@ -257,343 +221,181 @@ public class UserHistoryDictionary extends ExpandableDictionary { final long last = SettingsValues.getLastUserHistoryWriteTime(mPrefs, mLocale); final boolean initializing = last == 0; final long now = System.currentTimeMillis(); - // Load the words that correspond to the current input locale - final Cursor cursor = query(MAIN_COLUMN_LOCALE + "=?", new String[] { mLocale }); - if (null == cursor) return; - try { - // TODO: Call SQLiteDataBase.beginTransaction / SQLiteDataBase.endTransaction - if (cursor.moveToFirst()) { - final int word1Index = cursor.getColumnIndex(MAIN_COLUMN_WORD1); - final int word2Index = cursor.getColumnIndex(MAIN_COLUMN_WORD2); - final int fcIndex = cursor.getColumnIndex(COLUMN_FORGETTING_CURVE_VALUE); - while (!cursor.isAfterLast()) { - final String word1 = cursor.getString(word1Index); - final String word2 = cursor.getString(word2Index); - final int fc = cursor.getInt(fcIndex); + profTotal = 0; + final String fileName = NAME + "." + mLocale + ".dict"; + final ExpandableDictionary dictionary = this; + final OnAddWordListener listener = new OnAddWordListener() { + @Override + public void setUnigram(String word, String shortcutTarget, int frequency) { + profTotal++; + if (DBG_SAVE_RESTORE) { + Log.d(TAG, "load unigram: " + word + "," + frequency); + } + dictionary.addWord(word, shortcutTarget, frequency); + mBigramList.addBigram(null, word, (byte)frequency); + } + + @Override + public void setBigram(String word1, String word2, int frequency) { + if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH + && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) { + profTotal++; if (DBG_SAVE_RESTORE) { - Log.d(TAG, "--- Load user history: " + word1 + ", " + word2 + "," - + mLocale + "," + this); - } - // Safeguard against adding really long words. Stack may overflow due - // to recursive lookup - if (null == word1) { - super.addWord(word2, null /* shortcut */, fc); - } else if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH - && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) { - super.setBigramAndGetFrequency( - word1, word2, initializing ? new ForgettingCurveParams(true) - : new ForgettingCurveParams(fc, now, last)); + Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency); } - mBigramList.addBigram(word1, word2, (byte)fc); - cursor.moveToNext(); + dictionary.setBigramAndGetFrequency( + word1, word2, initializing ? new ForgettingCurveParams(true) + : new ForgettingCurveParams(frequency, now, last)); } + mBigramList.addBigram(word1, word2, (byte)frequency); } + }; + + // Load the dictionary from binary file + FileInputStream inStream = null; + try { + final File file = new File(getContext().getFilesDir(), fileName); + final byte[] buffer = new byte[(int)file.length()]; + inStream = new FileInputStream(file); + inStream.read(buffer); + UserHistoryDictIOUtils.readDictionaryBinary( + new UserHistoryDictIOUtils.ByteArrayWrapper(buffer), listener); + } catch (FileNotFoundException e) { + Log.e(TAG, "when loading: file not found" + e); + } catch (IOException e) { + Log.e(TAG, "IOException when open bytebuffer: " + e); } finally { - cursor.close(); + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + // do nothing + } + } if (PROFILE_SAVE_RESTORE) { final long diff = System.currentTimeMillis() - now; - Log.w(TAG, "PROF: Load User HistoryDictionary: " - + mLocale + ", " + diff + "ms."); + Log.d(TAG, "PROF: Load UserHistoryDictionary: " + + mLocale + ", " + diff + "ms. load " + profTotal + "entries."); } } } /** - * Query the database + * Async task to write pending words to the binarydicts. */ - private static Cursor query(String selection, String[] selectionArgs) { - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - - // main INNER JOIN frequency ON (main._id=freq.pair_id) - qb.setTables(MAIN_TABLE_NAME + " INNER JOIN " + FREQ_TABLE_NAME + " ON (" - + MAIN_TABLE_NAME + "." + MAIN_COLUMN_ID + "=" + FREQ_TABLE_NAME + "." - + FREQ_COLUMN_PAIR_ID +")"); - - qb.setProjectionMap(sDictProjectionMap); - - // Get the database and run the query - try { - SQLiteDatabase db = sOpenHelper.getReadableDatabase(); - Cursor c = qb.query(db, - new String[] { - MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD2, COLUMN_FORGETTING_CURVE_VALUE }, - selection, selectionArgs, null, null, null); - return c; - } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) { - // Can't open the database : presumably we can't access storage. That may happen - // when the device is wedged; do a best effort to still start the keyboard. - return null; - } - } - - /** - * This class helps open, create, and upgrade the database file. - */ - private static class DatabaseHelper extends SQLiteOpenHelper { - - DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("PRAGMA foreign_keys = ON;"); - db.execSQL("CREATE TABLE " + MAIN_TABLE_NAME + " (" - + MAIN_COLUMN_ID + " INTEGER PRIMARY KEY," - + MAIN_COLUMN_WORD1 + " TEXT," - + MAIN_COLUMN_WORD2 + " TEXT," - + MAIN_COLUMN_LOCALE + " TEXT" - + ");"); - db.execSQL("CREATE TABLE " + FREQ_TABLE_NAME + " (" - + FREQ_COLUMN_ID + " INTEGER PRIMARY KEY," - + FREQ_COLUMN_PAIR_ID + " INTEGER," - + COLUMN_FORGETTING_CURVE_VALUE + " INTEGER," - + "FOREIGN KEY(" + FREQ_COLUMN_PAIR_ID + ") REFERENCES " + MAIN_TABLE_NAME - + "(" + MAIN_COLUMN_ID + ")" + " ON DELETE CASCADE" - + ");"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.w(TAG, "Upgrading database from version " + oldVersion + " to " - + newVersion + ", which will destroy all old data"); - db.execSQL("DROP TABLE IF EXISTS " + MAIN_TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + FREQ_TABLE_NAME); - onCreate(db); - } - } - - /** - * Async task to write pending words to the database so that it stays in sync with - * the in-memory trie. - */ - private static class UpdateDbTask extends AsyncTask { + private static class UpdateBinaryTask extends AsyncTask + implements BigramDictionaryInterface { private final UserHistoryDictionaryBigramList mBigramList; - private final DatabaseHelper mDbHelper; + private final boolean mAddLevel0Bigrams; private final String mLocale; private final UserHistoryDictionary mUserHistoryDictionary; private final SharedPreferences mPrefs; + private final Context mContext; - public UpdateDbTask( - DatabaseHelper openHelper, UserHistoryDictionaryBigramList pendingWrites, - String locale, UserHistoryDictionary dict, SharedPreferences prefs) { + public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale, + UserHistoryDictionary dict, SharedPreferences prefs, Context context) { mBigramList = pendingWrites; mLocale = locale; - mDbHelper = openHelper; mUserHistoryDictionary = dict; mPrefs = prefs; - } - - /** Prune any old data if the database is getting too big. */ - private static void checkPruneData(SQLiteDatabase db) { - db.execSQL("PRAGMA foreign_keys = ON;"); - Cursor c = db.query(FREQ_TABLE_NAME, new String[] { FREQ_COLUMN_PAIR_ID }, - null, null, null, null, null); - try { - int totalRowCount = c.getCount(); - // prune out old data if we have too much data - if (totalRowCount > sMaxHistoryBigrams) { - int numDeleteRows = (totalRowCount - sMaxHistoryBigrams) - + sDeleteHistoryBigrams; - int pairIdColumnId = c.getColumnIndex(FREQ_COLUMN_PAIR_ID); - c.moveToFirst(); - int count = 0; - while (count < numDeleteRows && !c.isAfterLast()) { - String pairId = c.getString(pairIdColumnId); - // Deleting from MAIN table will delete the frequencies - // due to FOREIGN KEY .. ON DELETE CASCADE - db.delete(MAIN_TABLE_NAME, MAIN_COLUMN_ID + "=?", - new String[] { pairId }); - c.moveToNext(); - count++; - } - } - } finally { - c.close(); - } + mContext = context; + mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS; } @Override protected Void doInBackground(Void... v) { - SQLiteDatabase db = null; - if (mUserHistoryDictionary.mBigramListLock.tryLock()) { + if (mUserHistoryDictionary.isTest) { + // If isTest == true, wait until the lock is released. + mUserHistoryDictionary.mBigramListLock.lock(); try { - try { - db = mDbHelper.getWritableDatabase(); - } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) { - // If we can't open the db, don't do anything. Exit through the next test - // for non-nullity of the db variable. - } - if (null == db) { - // Not much we can do. Just exit. - return null; - } - db.beginTransaction(); - return doLoadTaskLocked(db); + doWriteTaskLocked(); } finally { - if (db != null) { - db.endTransaction(); - } mUserHistoryDictionary.mBigramListLock.unlock(); } + } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) { + doWriteTaskLocked(); } return null; } - private Void doLoadTaskLocked(SQLiteDatabase db) { + private void doWriteTaskLocked() { if (DBG_STRESS_TEST) { try { Log.w(TAG, "Start stress in closing: " + mLocale); Thread.sleep(15000); Log.w(TAG, "End stress in closing"); } catch (InterruptedException e) { + Log.e(TAG, "In stress test: " + e); } } + final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0; - int profTotal = 0; - int profInsert = 0; - int profDelete = 0; - db.execSQL("PRAGMA foreign_keys = ON;"); - final boolean addLevel0Bigram = mBigramList.size() <= sMaxHistoryBigrams; - - // Write all the entries to the db - for (String word1 : mBigramList.keySet()) { - final HashMap word1Bigrams = mBigramList.getBigrams(word1); - for (String word2 : word1Bigrams.keySet()) { - if (PROFILE_SAVE_RESTORE) { - ++profTotal; - } - // Get new frequency. Do not insert unigrams/bigrams which freq is "-1". - final int freq; // -1, or 0~255 - if (word1 == null) { // unigram - freq = FREQUENCY_FOR_TYPED; - final byte prevFc = word1Bigrams.get(word2); - if (prevFc == FREQUENCY_FOR_TYPED) { - // No need to update since we found no changes for this entry. - // Just skip to the next entry. - if (DBG_SAVE_RESTORE) { - Log.d(TAG, "Skip update user history: " + word1 + "," + word2 - + "," + prevFc); - } - if (!DBG_ALWAYS_WRITE) { - continue; - } - } - } else { // bigram - final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2); - if (nw != null) { - final ForgettingCurveParams fcp = nw.getFcParams(); - final byte prevFc = word1Bigrams.get(word2); - final byte fc = (byte)fcp.getFc(); - final boolean isValid = fcp.isValid(); - if (prevFc > 0 && prevFc == fc) { - // No need to update since we found no changes for this entry. - // Just skip to the next entry. - if (DBG_SAVE_RESTORE) { - Log.d(TAG, "Skip update user history: " + word1 + "," - + word2 + "," + prevFc); - } - if (!DBG_ALWAYS_WRITE) { - continue; - } else { - freq = fc; - } - } else if (UserHistoryForgettingCurveUtils. - needsToSave(fc, isValid, addLevel0Bigram)) { - freq = fc; - } else { - // Delete this entry - freq = -1; - } - } else { - // Delete this entry - freq = -1; - } - } - // TODO: this process of making a text search for each pair each time - // is terribly inefficient. Optimize this. - // Find pair id - Cursor c = null; + final String fileName = NAME + "." + mLocale + ".dict"; + final File file = new File(mContext.getFilesDir(), fileName); + FileOutputStream out = null; + + try { + out = new FileOutputStream(file); + UserHistoryDictIOUtils.writeDictionaryBinary(out, this, mBigramList, VERSION3); + out.flush(); + out.close(); + } catch (IOException e) { + Log.e(TAG, "IO Exception while writing file: " + e); + } finally { + if (out != null) { try { - if (null != word1) { - c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID }, - MAIN_COLUMN_WORD1 + "=? AND " + MAIN_COLUMN_WORD2 + "=? AND " - + MAIN_COLUMN_LOCALE + "=?", - new String[] { word1, word2, mLocale }, null, null, - null); - } else { - c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID }, - MAIN_COLUMN_WORD1 + " IS NULL AND " + MAIN_COLUMN_WORD2 - + "=? AND " + MAIN_COLUMN_LOCALE + "=?", - new String[] { word2, mLocale }, null, null, null); - } - - final int pairId; - if (c.moveToFirst()) { - if (PROFILE_SAVE_RESTORE) { - ++profDelete; - } - // Delete existing pair - pairId = c.getInt(c.getColumnIndex(MAIN_COLUMN_ID)); - db.delete(FREQ_TABLE_NAME, FREQ_COLUMN_PAIR_ID + "=?", - new String[] { Integer.toString(pairId) }); - } else { - // Create new pair - Long pairIdLong = db.insert(MAIN_TABLE_NAME, null, - getContentValues(word1, word2, mLocale)); - pairId = pairIdLong.intValue(); - } - // Eliminate freq == 0 because that word is profanity. - if (freq > 0) { - if (PROFILE_SAVE_RESTORE) { - ++profInsert; - } - if (DBG_SAVE_RESTORE) { - Log.d(TAG, "--- Save user history: " + word1 + ", " + word2 - + mLocale + "," + this); - } - // Insert new frequency - db.insert(FREQ_TABLE_NAME, null, - getFrequencyContentValues(pairId, freq)); - // Update an existing bigram entry in mBigramList too in order to - // synchronize the SQL DB and mBigramList. - mBigramList.updateBigram(word1, word2, (byte)freq); - } - } finally { - if (c != null) { - c.close(); - } + out.close(); + } catch (IOException e) { + // ignore } } } - checkPruneData(db); - // Save the timestamp after we finish writing the SQL DB. + // Save the timestamp after we finish writing the binary dictionary. SettingsValues.setLastUserHistoryWriteTime(mPrefs, mLocale); if (PROFILE_SAVE_RESTORE) { final long diff = System.currentTimeMillis() - now; - Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", "+ diff - + "ms. Total: " + profTotal + ". Insert: " + profInsert + ". Delete: " - + profDelete); + Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", " + diff + "ms."); } - db.setTransactionSuccessful(); - return null; } - private static ContentValues getContentValues(String word1, String word2, String locale) { - ContentValues values = new ContentValues(3); - values.put(MAIN_COLUMN_WORD1, word1); - values.put(MAIN_COLUMN_WORD2, word2); - values.put(MAIN_COLUMN_LOCALE, locale); - return values; + @Override + public int getFrequency(String word1, String word2) { + final int freq; + if (word1 == null) { // unigram + freq = FREQUENCY_FOR_TYPED; + final byte prevFc = mBigramList.getBigrams(word1).get(word2); + } else { // bigram + final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2); + if (nw != null) { + final ForgettingCurveParams fcp = nw.getFcParams(); + final byte prevFc = mBigramList.getBigrams(word1).get(word2); + final byte fc = fcp.getFc(); + final boolean isValid = fcp.isValid(); + if (prevFc > 0 && prevFc == fc) { + freq = ((int)fc) & 0xFF; + } else if (UserHistoryForgettingCurveUtils. + needsToSave(fc, isValid, mAddLevel0Bigrams)) { + freq = ((int)fc) & 0xFF; + } else { + // Delete this entry + freq = -1; + } + } else { + // Delete this entry + freq = -1; + } + } + return freq; } + } - private static ContentValues getFrequencyContentValues(int pairId, int frequency) { - ContentValues values = new ContentValues(2); - values.put(FREQ_COLUMN_PAIR_ID, pairId); - values.put(COLUMN_FORGETTING_CURVE_VALUE, frequency); - return values; + void forceAddWordForTest(final String word1, final String word2, final boolean isValid) { + mBigramListLock.lock(); + try { + addToUserHistory(word1, word2, isValid); + } finally { + mBigramListLock.unlock(); } } - } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index b97be0543..a1606db60 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -77,7 +77,10 @@ public class BinaryDictIOUtils { p.mAddress += BinaryDictInputOutput.getGroupCountSize(p.mNumOfCharGroup); p.mPosition = 0; } - + if (p.mNumOfCharGroup == 0) { + stack.pop(); + continue; + } CharGroupInfo info = BinaryDictInputOutput.readCharGroup(buffer, p.mAddress - headerSize, formatOptions); for (int i = 0; i < info.mCharacters.length; ++i) { diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java new file mode 100644 index 000000000..f2a17d206 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 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.latin; + +import com.android.inputmethod.latin.UserHistoryDictionary; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.test.AndroidTestCase; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +/** + * Unit tests for UserHistoryDictionary + */ +public class UserHistoryDictionaryTests extends AndroidTestCase { + private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); + private SharedPreferences mPrefs; + + private static final String[] CHARACTERS = { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" + }; + + @Override + public void setUp() { + mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + } + + /** + * Generates a random word. + */ + private String generateWord(final int value) { + final int lengthOfChars = CHARACTERS.length; + StringBuilder builder = new StringBuilder(); + long lvalue = Math.abs((long)value); + while (lvalue > 0) { + builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]); + lvalue /= lengthOfChars; + } + return builder.toString(); + } + + private List generateWords(final int number, final Random random) { + final Set wordSet = CollectionUtils.newHashSet(); + while (wordSet.size() < number) { + wordSet.add(generateWord(random.nextInt())); + } + return new ArrayList(wordSet); + } + + private void addToDict(final UserHistoryDictionary dict, final List words) { + String prevWord = null; + for (String word : words) { + dict.forceAddWordForTest(prevWord, word, true); + prevWord = word; + } + } + + public void testRandomWords() { + Log.d(TAG, "This test can be used for profiling."); + Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true."); + final int numberOfWords = 1000; + final Random random = new Random(123456); + List words = generateWords(numberOfWords, random); + + final String locale = "testRandomWords"; + final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(), + locale, mPrefs); + dict.isTest = true; + + addToDict(dict, words); + + try { + Log.d(TAG, "waiting for adding the word ..."); + Thread.sleep(2000); + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: " + e); + } + + // write to file + dict.close(); + + try { + Log.d(TAG, "waiting for writing ..."); + Thread.sleep(5000); + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: " + e); + } + } +} -- cgit v1.2.3-83-g751a From 17752016713b92a55e9c2356d07b7ed51c67416b Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 27 Sep 2012 15:27:51 +0900 Subject: Calculate default touch position correction data for keys on 4th row This change also disables touch position correction for Phone/Dvorak keyboard. Bug: 7243101 Change-Id: Idfcc7ad1feabbe6a81635dceba5be087401fa18b --- .../values-sw600dp/touch-position-correction.xml | 60 ++++++++++++++++++++++ java/res/values/styles.xml | 2 +- java/res/values/touch-position-correction.xml | 18 ++++--- java/res/xml-land/kbd_number.xml | 1 + java/res/xml-land/kbd_phone.xml | 1 + java/res/xml-land/kbd_phone_symbols.xml | 1 + java/res/xml-sw600dp-land/kbd_number.xml | 1 + java/res/xml-sw600dp-land/kbd_phone.xml | 1 + java/res/xml-sw600dp-land/kbd_phone_symbols.xml | 1 + java/res/xml-sw600dp/kbd_10_10_7_symbols.xml | 1 + java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml | 1 + java/res/xml-sw600dp/kbd_number.xml | 1 + java/res/xml-sw600dp/kbd_phone.xml | 1 + java/res/xml-sw600dp/kbd_phone_symbols.xml | 1 + java/res/xml-sw768dp-land/kbd_number.xml | 1 + java/res/xml-sw768dp-land/kbd_phone.xml | 1 + java/res/xml-sw768dp-land/kbd_phone_symbols.xml | 1 + java/res/xml-sw768dp/kbd_number.xml | 1 + java/res/xml-sw768dp/kbd_phone.xml | 1 + java/res/xml-sw768dp/kbd_phone_symbols.xml | 1 + java/res/xml-sw768dp/kbd_thai_symbols.xml | 2 +- java/res/xml-sw768dp/kbd_thai_symbols_shift.xml | 2 +- java/res/xml/kbd_10_10_7_symbols.xml | 1 + java/res/xml/kbd_10_10_7_symbols_shift.xml | 1 + java/res/xml/kbd_number.xml | 1 + java/res/xml/kbd_pcqwerty.xml | 2 +- java/res/xml/kbd_pcqwerty_symbols.xml | 2 +- java/res/xml/kbd_phone.xml | 1 + java/res/xml/kbd_phone_symbols.xml | 1 + java/res/xml/kbd_symbols.xml | 1 + java/res/xml/kbd_symbols_shift.xml | 1 + java/res/xml/kbd_thai.xml | 2 +- java/res/xml/kbd_thai_symbols.xml | 1 + java/res/xml/kbd_thai_symbols_shift.xml | 1 + .../inputmethod/keyboard/ProximityInfo.java | 32 ++++++------ .../keyboard/internal/TouchPositionCorrection.java | 6 ++- 36 files changed, 124 insertions(+), 30 deletions(-) create mode 100644 java/res/values-sw600dp/touch-position-correction.xml (limited to 'java/src') diff --git a/java/res/values-sw600dp/touch-position-correction.xml b/java/res/values-sw600dp/touch-position-correction.xml new file mode 100644 index 000000000..f77d3ae83 --- /dev/null +++ b/java/res/values-sw600dp/touch-position-correction.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index d52039221..928c5f5d0 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -19,7 +19,7 @@