From 00da3084bfe3c815af9f0b3053d4c4aad7190b7c Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Sat, 21 Jul 2012 17:34:38 -0700 Subject: [PATCH 001/133] Unnessary LOCAL_MODULE_TAGS Change-Id: Idaf766c4b5b45da782e15750cbefa9d9ec1f0487 --- tools/maketext/Android.mk | 1 - tools/maketext/etc/Android.mk | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/maketext/Android.mk b/tools/maketext/Android.mk index 98731b718..77914cae6 100644 --- a/tools/maketext/Android.mk +++ b/tools/maketext/Android.mk @@ -19,7 +19,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES += $(call all-java-files-under,src) LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_JAVA_RESOURCE_DIRS := res -LOCAL_MODULE_TAGS := eng LOCAL_MODULE := maketext include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/tools/maketext/etc/Android.mk b/tools/maketext/etc/Android.mk index 4fa194bcd..475676b3a 100644 --- a/tools/maketext/etc/Android.mk +++ b/tools/maketext/etc/Android.mk @@ -15,7 +15,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := eng - LOCAL_PREBUILT_EXECUTABLES := maketext + include $(BUILD_HOST_PREBUILT) From 7ae1fd02d40c8c026a411f1037753725868c611e Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 7 Aug 2012 14:30:28 +0900 Subject: [PATCH 002/133] Use ArrayList to implement PointerTrackerQueue Change-Id: Ie386e6c7869df4df940e09fccffc5cd661134557 --- .../inputmethod/keyboard/PointerTracker.java | 2 +- .../internal/PointerTrackerQueue.java | 161 +++++++++++++----- .../internal/PointerTrackerQueueTests.java | 2 +- 3 files changed, 116 insertions(+), 49 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 184011ffe..2b90a3cb9 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -34,7 +34,7 @@ import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; -public class PointerTracker implements PointerTrackerQueue.ElementActions { +public class PointerTracker implements PointerTrackerQueue.Element { private static final String TAG = PointerTracker.class.getSimpleName(); private static final boolean DEBUG_EVENT = false; private static final boolean DEBUG_MOVE_EVENT = false; diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index bd1648014..1c7ceaf92 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -18,85 +18,146 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.ArrayList; public class PointerTrackerQueue { private static final String TAG = PointerTrackerQueue.class.getSimpleName(); private static final boolean DEBUG = false; - public interface ElementActions { + public interface Element { public boolean isModifier(); public boolean isInSlidingKeyInput(); public void onPhantomUpEvent(long eventTime); } - // TODO: Use ring buffer instead of {@link LinkedList}. - private final LinkedList mQueue = new LinkedList(); + private static final int INITIAL_CAPACITY = 10; + private final ArrayList mExpandableArrayOfActivePointers = + new ArrayList(INITIAL_CAPACITY); + private int mArraySize = 0; - public int size() { - return mQueue.size(); + public synchronized int size() { + return mArraySize; } - public synchronized void add(ElementActions tracker) { - mQueue.add(tracker); + public synchronized void add(final Element pointer) { + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + if (arraySize < expandableArray.size()) { + expandableArray.set(arraySize, pointer); + } else { + expandableArray.add(pointer); + } + mArraySize = arraySize + 1; } - public synchronized void remove(ElementActions tracker) { - mQueue.remove(tracker); + public synchronized void remove(final Element pointer) { + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + int newSize = 0; + for (int index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element == pointer) { + if (newSize != index) { + Log.w(TAG, "Found duplicated element in remove: " + pointer); + } + continue; // Remove this element from the expandableArray. + } + if (newSize != index) { + // Shift this element toward the beginning of the expandableArray. + expandableArray.set(newSize, element); + } + newSize++; + } + mArraySize = newSize; } - public synchronized void releaseAllPointersOlderThan(ElementActions tracker, - long eventTime) { + public synchronized void releaseAllPointersOlderThan(final Element pointer, + final long eventTime) { if (DEBUG) { - Log.d(TAG, "releaseAllPoniterOlderThan: " + tracker + " " + this); + Log.d(TAG, "releaseAllPoniterOlderThan: " + pointer + " " + this); } - if (!mQueue.contains(tracker)) { - return; - } - final Iterator it = mQueue.iterator(); - while (it.hasNext()) { - final ElementActions t = it.next(); - if (t == tracker) { - break; + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + int newSize, index; + for (newSize = index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element == pointer) { + break; // Stop releasing elements. } - if (!t.isModifier()) { - t.onPhantomUpEvent(eventTime); - it.remove(); + if (!element.isModifier()) { + element.onPhantomUpEvent(eventTime); + continue; // Remove this element from the expandableArray. + } + if (newSize != index) { + // Shift this element toward the beginning of the expandableArray. + expandableArray.set(newSize, element); + } + newSize++; + } + // Shift rest of the expandableArray. + int count = 0; + for (; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element == pointer) { + if (count > 0) { + Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: " + + pointer); + } + count++; + } + if (newSize != index) { + expandableArray.set(newSize, expandableArray.get(index)); + newSize++; } } + mArraySize = newSize; } - public void releaseAllPointers(long eventTime) { + public void releaseAllPointers(final long eventTime) { releaseAllPointersExcept(null, eventTime); } - public synchronized void releaseAllPointersExcept(ElementActions tracker, long eventTime) { + public synchronized void releaseAllPointersExcept(final Element pointer, + final long eventTime) { if (DEBUG) { - if (tracker == null) { + if (pointer == null) { Log.d(TAG, "releaseAllPoniters: " + this); } else { - Log.d(TAG, "releaseAllPoniterExcept: " + tracker + " " + this); + Log.d(TAG, "releaseAllPoniterExcept: " + pointer + " " + this); } } - final Iterator it = mQueue.iterator(); - while (it.hasNext()) { - final ElementActions t = it.next(); - if (t != tracker) { - t.onPhantomUpEvent(eventTime); - it.remove(); + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + int newSize = 0, count = 0; + for (int index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element == pointer) { + if (count > 0) { + Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: " + pointer); + } + count++; + } else { + element.onPhantomUpEvent(eventTime); + continue; // Remove this element from the expandableArray. } + if (newSize != index) { + // Shift this element toward the beginning of the expandableArray. + expandableArray.set(newSize, element); + } + newSize++; } + mArraySize = newSize; } - public synchronized boolean hasModifierKeyOlderThan(ElementActions tracker) { - final Iterator it = mQueue.iterator(); - while (it.hasNext()) { - final ElementActions t = it.next(); - if (t == tracker) { - break; + public synchronized boolean hasModifierKeyOlderThan(final Element pointer) { + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + for (int index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element == pointer) { + return false; // Stop searching modifier key. } - if (t.isModifier()) { + if (element.isModifier()) { return true; } } @@ -104,8 +165,11 @@ public class PointerTrackerQueue { } public synchronized boolean isAnyInSlidingKeyInput() { - for (final ElementActions tracker : mQueue) { - if (tracker.isInSlidingKeyInput()) { + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + for (int index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); + if (element.isInSlidingKeyInput()) { return true; } } @@ -113,12 +177,15 @@ public class PointerTrackerQueue { } @Override - public String toString() { + public synchronized String toString() { final StringBuilder sb = new StringBuilder(); - for (final ElementActions tracker : mQueue) { + final ArrayList expandableArray = mExpandableArrayOfActivePointers; + final int arraySize = mArraySize; + for (int index = 0; index < arraySize; index++) { + final Element element = expandableArray.get(index); if (sb.length() > 0) sb.append(" "); - sb.append(tracker.toString()); + sb.append(element.toString()); } return "[" + sb.toString() + "]"; } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java index 99fbc967d..8fed28f9e 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java @@ -19,7 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.test.AndroidTestCase; public class PointerTrackerQueueTests extends AndroidTestCase { - public static class Element implements PointerTrackerQueue.ElementActions { + public static class Element implements PointerTrackerQueue.Element { public static int sPhantomUpCount; public static final long NOT_HAPPENED = -1; From 79eefda0d3ab5e03c2d012ed8ea1898004781871 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 8 Aug 2012 15:55:26 +0900 Subject: [PATCH 003/133] Add SuggestInitializationListener to BinaryDictionary.resetMainDict() Change-Id: I4c01f172ddb2ca60fa6e604384610c90b76e5457 --- .../android/inputmethod/latin/LatinIME.java | 2 +- .../android/inputmethod/latin/Suggest.java | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 455086015..43901bab4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -517,7 +517,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen /* package private */ void resetSuggestMainDict() { final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - mSuggest.resetMainDict(this, subtypeLocale); + mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */); mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 5e2a04124..a65d36adb 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -60,13 +60,11 @@ public class Suggest { // Locale used for upper- and title-casing words private final Locale mLocale; - private final SuggestInitializationListener mListener; public Suggest(final Context context, final Locale locale, final SuggestInitializationListener listener) { - initAsynchronously(context, locale); + initAsynchronously(context, locale, listener); mLocale = locale; - mListener = listener; } /* package for test */ Suggest(final Context context, final File dictionary, @@ -74,7 +72,6 @@ public class Suggest { final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length /* useFullEditDistance */, false, locale); mLocale = locale; - mListener = null; mMainDictionary = mainDict; addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); initWhitelistAndAutocorrectAndPool(context, locale); @@ -85,8 +82,9 @@ public class Suggest { addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary); } - private void initAsynchronously(final Context context, final Locale locale) { - resetMainDict(context, locale); + private void initAsynchronously(final Context context, final Locale locale, + final SuggestInitializationListener listener) { + resetMainDict(context, locale, listener); // TODO: read the whitelist and init the pool asynchronously too. // initPool should be done asynchronously now that the pool is thread-safe. @@ -104,10 +102,11 @@ public class Suggest { } } - public void resetMainDict(final Context context, final Locale locale) { + public void resetMainDict(final Context context, final Locale locale, + final SuggestInitializationListener listener) { mMainDictionary = null; - if (mListener != null) { - mListener.onUpdateMainDictionaryAvailability(hasMainDictionary()); + if (listener != null) { + listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); } new Thread("InitializeBinaryDictionary") { @Override @@ -116,8 +115,8 @@ public class Suggest { DictionaryFactory.createMainDictionaryFromManager(context, locale); addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict); mMainDictionary = newMainDict; - if (mListener != null) { - mListener.onUpdateMainDictionaryAvailability(hasMainDictionary()); + if (listener != null) { + listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); } } }.start(); From 35336cf83a434514c60d79389ceb818cba642506 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 8 Aug 2012 15:59:56 +0900 Subject: [PATCH 004/133] Add toString() method to Keyboard Change-Id: I74c8f006323ed78ec67414fd17849ab40966968e --- java/src/com/android/inputmethod/keyboard/Keyboard.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 3abe890cb..919850095 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -219,6 +219,11 @@ public class Keyboard { return code >= CODE_SPACE; } + @Override + public String toString() { + return mId.toString(); + } + public static class Params { public KeyboardId mId; public int mThemeId; From 9b392364b7c59a143e66a8ef1d1149a47843d124 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Wed, 8 Aug 2012 16:46:16 +0900 Subject: [PATCH 005/133] Cleanup headers. Change-Id: Ied1bedb8ba5fae590987ea23bed32f4db1a60d77 --- ...com_android_inputmethod_keyboard_ProximityInfo.cpp | 11 ++++------- native/jni/jni_common.cpp | 4 ++-- native/jni/src/proximity_info.cpp | 4 ++-- native/jni/src/proximity_info.h | 2 +- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index 74390ccdf..0f2c2da43 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -16,8 +16,6 @@ #define LOG_TAG "LatinIME: jni: ProximityInfo" -#include - #include "com_android_inputmethod_keyboard_ProximityInfo.h" #include "jni.h" #include "jni_common.h" @@ -26,14 +24,13 @@ namespace latinime { static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, - jstring localejStr, jint maxProximityCharsSize, jint displayWidth, jint displayHeight, + jstring localeJStr, jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth, jint gridHeight, jint mostCommonkeyWidth, jintArray proximityCharsArray, jint keyCount, jintArray keyXCoordinateArray, jintArray keyYCoordinateArray, jintArray keyWidthArray, jintArray keyHeightArray, jintArray keyCharCodeArray, jfloatArray sweetSpotCenterXArray, jfloatArray sweetSpotCenterYArray, jfloatArray sweetSpotRadiusArray) { - const char *localeStrPtr = env->GetStringUTFChars(localejStr, 0); - const std::string localeStr(localeStrPtr); + const char *localeCStr = env->GetStringUTFChars(localeJStr, 0); jint *proximityChars = env->GetIntArrayElements(proximityCharsArray, 0); jint *keyXCoordinates = safeGetIntArrayElements(env, keyXCoordinateArray); jint *keyYCoordinates = safeGetIntArrayElements(env, keyYCoordinateArray); @@ -44,7 +41,7 @@ static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, jfloat *sweetSpotCenterYs = safeGetFloatArrayElements(env, sweetSpotCenterYArray); jfloat *sweetSpotRadii = safeGetFloatArrayElements(env, sweetSpotRadiusArray); ProximityInfo *proximityInfo = new ProximityInfo( - localeStr, maxProximityCharsSize, displayWidth, displayHeight, gridWidth, gridHeight, + localeCStr, maxProximityCharsSize, displayWidth, displayHeight, gridWidth, gridHeight, mostCommonkeyWidth, (const int32_t*)proximityChars, keyCount, (const int32_t*)keyXCoordinates, (const int32_t*)keyYCoordinates, (const int32_t*)keyWidths, (const int32_t*)keyHeights, (const int32_t*)keyCharCodes, @@ -59,7 +56,7 @@ static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, safeReleaseIntArrayElements(env, keyYCoordinateArray, keyYCoordinates); safeReleaseIntArrayElements(env, keyXCoordinateArray, keyXCoordinates); env->ReleaseIntArrayElements(proximityCharsArray, proximityChars, 0); - env->ReleaseStringUTFChars(localejStr, localeStrPtr); + env->ReleaseStringUTFChars(localeJStr, localeCStr); return (jlong)proximityInfo; } diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp index 105a4dc4c..d45f97a52 100644 --- a/native/jni/jni_common.cpp +++ b/native/jni/jni_common.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "LatinIME: jni" +#include + #include "com_android_inputmethod_keyboard_ProximityInfo.h" #include "com_android_inputmethod_latin_BinaryDictionary.h" #include "com_android_inputmethod_latin_NativeUtils.h" @@ -23,8 +25,6 @@ #include "jni.h" #include "jni_common.h" -#include - using namespace latinime; /* diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index cee408d46..e9cb14e63 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -36,7 +36,7 @@ inline void copyOrFillZero(void *to, const void *from, size_t size) { } } -ProximityInfo::ProximityInfo(const std::string localeStr, const int maxProximityCharsSize, +ProximityInfo::ProximityInfo(const char *localeCStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, const int gridHeight, const int mostCommonKeyWidth, const int32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, @@ -52,7 +52,7 @@ ProximityInfo::ProximityInfo(const std::string localeStr, const int maxProximity HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs && sweetSpotCenterYs && sweetSpotRadii), - mLocaleStr(localeStr) { + mLocaleStr(localeCStr) { const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; if (DEBUG_PROXIMITY_INFO) { AKLOGI("Create proximity info array %d", proximityGridLength); diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index abd07dd3e..6c5c6bf99 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -28,7 +28,7 @@ class Correction; class ProximityInfo { public: - ProximityInfo(const std::string localeStr, const int maxProximityCharsSize, + ProximityInfo(const char *localeCStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, const int gridHeight, const int mostCommonkeyWidth, const int32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, From 4c573f3c649201b3e0a8198b6aefc0f69c158611 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 8 Aug 2012 17:15:58 +0900 Subject: [PATCH 006/133] Rename dicttool to dicttool_aosp and split commands Change-Id: I83d948be2db5cd0203e876f019697e1d5e038818 --- tools/dicttool/Android.mk | 2 +- tools/dicttool/etc/Android.mk | 2 +- .../dicttool/etc/{dicttool => dicttool_aosp} | 2 +- .../dicttool/etc/{makedict => makedict_aosp} | 2 +- .../latin/dicttool/AdditionalCommandList.java | 22 ++++++++++++++++ .../latin/dicttool/CommandList.java | 26 +++++++++++++++++++ .../inputmethod/latin/dicttool/Dicttool.java | 9 ++++--- 7 files changed, 57 insertions(+), 8 deletions(-) rename tools/dicttool/etc/{dicttool => dicttool_aosp} (98%) rename tools/dicttool/etc/{makedict => makedict_aosp} (96%) create mode 100644 tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java create mode 100644 tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index e9c11acc4..df8cb1030 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -24,7 +24,7 @@ LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \ $(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \ $(call all-java-files-under,tests) LOCAL_JAR_MANIFEST := etc/manifest.txt -LOCAL_MODULE := dicttool +LOCAL_MODULE := dicttool_aosp LOCAL_JAVA_LIBRARIES := junit LOCAL_MODULE_TAGS := eng diff --git a/tools/dicttool/etc/Android.mk b/tools/dicttool/etc/Android.mk index 1eab70fc1..8952827ab 100644 --- a/tools/dicttool/etc/Android.mk +++ b/tools/dicttool/etc/Android.mk @@ -16,5 +16,5 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng -LOCAL_PREBUILT_EXECUTABLES := dicttool makedict +LOCAL_PREBUILT_EXECUTABLES := dicttool_aosp makedict_aosp include $(BUILD_HOST_PREBUILT) diff --git a/tools/dicttool/etc/dicttool b/tools/dicttool/etc/dicttool_aosp similarity index 98% rename from tools/dicttool/etc/dicttool rename to tools/dicttool/etc/dicttool_aosp index 8a39694f7..a4879a279 100755 --- a/tools/dicttool/etc/dicttool +++ b/tools/dicttool/etc/dicttool_aosp @@ -33,7 +33,7 @@ progdir=`pwd` prog="${progdir}"/`basename "${prog}"` cd "${oldwd}" -jarfile=dicttool.jar +jarfile=dicttool_aosp.jar frameworkdir="$progdir" if [ ! -r "$frameworkdir/$jarfile" ] then diff --git a/tools/dicttool/etc/makedict b/tools/dicttool/etc/makedict_aosp similarity index 96% rename from tools/dicttool/etc/makedict rename to tools/dicttool/etc/makedict_aosp index fffeb2376..095c50538 100755 --- a/tools/dicttool/etc/makedict +++ b/tools/dicttool/etc/makedict_aosp @@ -15,4 +15,4 @@ # Dicttool supports making the dictionary using the 'makedict' command and # the same arguments that the old 'makedict' command used to accept. -dicttool makedict $@ +dicttool_aosp makedict $@ diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java new file mode 100644 index 000000000..8d4eb751b --- /dev/null +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java @@ -0,0 +1,22 @@ +/** + * 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.dicttool; + +public class AdditionalCommandList { + public static void populate() { + } +} diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java new file mode 100644 index 000000000..d16b069fe --- /dev/null +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java @@ -0,0 +1,26 @@ +/** + * 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.dicttool; + +public class CommandList { + public static void populate() { + Dicttool.addCommand("info", Info.class); + Dicttool.addCommand("compress", Compress.Compressor.class); + Dicttool.addCommand("uncompress", Compress.Uncompressor.class); + Dicttool.addCommand("makedict", Makedict.class); + } +} diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java index c14ce7b88..bf417fb5a 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java @@ -32,10 +32,11 @@ public class Dicttool { static HashMap> sCommands = new HashMap>(); static { - sCommands.put("info", Info.class); - sCommands.put("compress", Compress.Compressor.class); - sCommands.put("uncompress", Compress.Uncompressor.class); - sCommands.put("makedict", Makedict.class); + CommandList.populate(); + AdditionalCommandList.populate(); + } + public static void addCommand(final String commandName, final Class cls) { + sCommands.put(commandName, cls); } private static Command getCommandInstance(final String commandName) { From c4e4dfe17dd60d200b851d228ce718543900a1fc Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Wed, 8 Aug 2012 19:40:44 +0900 Subject: [PATCH 007/133] Add traverse session Bug: 6942265 Change-Id: I384cf701f5ee44c4b1037e5b8ddfa3d570286f55 --- .../inputmethod/latin/BinaryDictionary.java | 3 + .../inputmethod/latin/DicTraverseSession.java | 59 +++++++++++++++++++ native/jni/Android.mk | 1 + ...d_inputmethod_latin_DicTraverseSession.cpp | 49 +++++++++++++++ ...oid_inputmethod_latin_DicTraverseSession.h | 46 +++++++++++++++ native/jni/jni_common.cpp | 6 ++ 6 files changed, 164 insertions(+) create mode 100644 java/src/com/android/inputmethod/latin/DicTraverseSession.java create mode 100644 native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp create mode 100644 native/jni/com_android_inputmethod_latin_DicTraverseSession.h diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 534cffb2d..7e6c53e2f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -58,6 +58,7 @@ public class BinaryDictionary extends Dictionary { private final int[] mOutputTypes = new int[MAX_RESULTS]; private final boolean mUseFullEditDistance; + private final DicTraverseSession mDicTraverseSession; /** * Constructor for the binary dictionary. This is supposed to be called from the @@ -76,6 +77,7 @@ public class BinaryDictionary extends Dictionary { super(dictType); mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length); + mDicTraverseSession = new DicTraverseSession(locale); } static { @@ -187,6 +189,7 @@ public class BinaryDictionary extends Dictionary { @Override public synchronized void close() { + mDicTraverseSession.close(); closeInternal(); } diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java new file mode 100644 index 000000000..d0c70f3ad --- /dev/null +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -0,0 +1,59 @@ +/* + * 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 java.util.Locale; + +public class DicTraverseSession { + static { + JniUtils.loadNativeLibrary(); + } + + private long mNativeDicTraverseSession; + + public DicTraverseSession(Locale locale) { + mNativeDicTraverseSession = createNativeDicTraverseSession( + locale != null ? locale.toString() : ""); + } + + private native long setDicTraverseSessionNative(String locale); + private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); + + private final long createNativeDicTraverseSession(String locale) { + return setDicTraverseSessionNative(locale); + } + + private void closeInternal() { + if (mNativeDicTraverseSession != 0) { + releaseDicTraverseSessionNative(mNativeDicTraverseSession); + mNativeDicTraverseSession = 0; + } + } + + public void close() { + closeInternal(); + } + + @Override + protected void finalize() throws Throwable { + try { + closeInternal(); + } finally { + super.finalize(); + } + } +} diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 9f9958377..1b7301b19 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -36,6 +36,7 @@ LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function LATIN_IME_JNI_SRC_FILES := \ com_android_inputmethod_keyboard_ProximityInfo.cpp \ com_android_inputmethod_latin_BinaryDictionary.cpp \ + com_android_inputmethod_latin_DicTraverseSession.cpp \ com_android_inputmethod_latin_NativeUtils.cpp \ jni_common.cpp diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp new file mode 100644 index 000000000..8ee0450e4 --- /dev/null +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#define LOG_TAG "LatinIME: jni: Session" + +#include "com_android_inputmethod_latin_DicTraverseSession.h" +#include "jni.h" +#include "jni_common.h" + +namespace latinime { +void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)() = 0; +void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; + +static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, + jstring localejStr) { + void *session = DicTraverseWrapper::getDicTraverseSession(); + return reinterpret_cast(session); +} + +static void latinime_DicTraverseSession_release(JNIEnv *env, jobject object, jlong session) { + void *pi = reinterpret_cast(session); + if (!pi) return; + DicTraverseWrapper::releaseDicTraverseSession(pi); +} + +static JNINativeMethod sMethods[] = { + {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, + {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_DicTraverseSession_release} +}; + +int register_DicTraverseSession(JNIEnv *env) { + const char *const kClassPathName = "com/android/inputmethod/latin/DicTraverseSession"; + return registerNativeMethods(env, kClassPathName, sMethods, + sizeof(sMethods) / sizeof(sMethods[0])); +} +} // namespace latinime diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h new file mode 100644 index 000000000..5238fd0af --- /dev/null +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H +#define _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H + +#include "defines.h" +#include "jni.h" + +namespace latinime { + +// TODO: Remove +class DicTraverseWrapper { + public: + static void *getDicTraverseSession() { + if (sDicTraverseSessionFactoryMethod) { + return sDicTraverseSessionFactoryMethod(); + } + return 0; + } + static void releaseDicTraverseSession(void *session) { + if (sDicTraverseSessionReleaseMethod) { + sDicTraverseSessionReleaseMethod(session); + } + } + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); + static void *(*sDicTraverseSessionFactoryMethod)(); + static void (*sDicTraverseSessionReleaseMethod)(void *); +}; +int register_DicTraverseSession(JNIEnv *env); +} // namespace latinime +#endif // _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp index 105a4dc4c..dde5f644e 100644 --- a/native/jni/jni_common.cpp +++ b/native/jni/jni_common.cpp @@ -18,6 +18,7 @@ #include "com_android_inputmethod_keyboard_ProximityInfo.h" #include "com_android_inputmethod_latin_BinaryDictionary.h" +#include "com_android_inputmethod_latin_DicTraverseSession.h" #include "com_android_inputmethod_latin_NativeUtils.h" #include "defines.h" #include "jni.h" @@ -45,6 +46,11 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { goto bail; } + if (!register_DicTraverseSession(env)) { + AKLOGE("ERROR: DicTraverseSession native registration failed"); + goto bail; + } + if (!register_ProximityInfo(env)) { AKLOGE("ERROR: ProximityInfo native registration failed"); goto bail; From bb005f787f4e00bd832e6a78797be10af2994061 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Wed, 8 Aug 2012 20:43:47 +0900 Subject: [PATCH 008/133] Start using JNI's GetArrayRegion() Change-Id: Iaa1014efe639dd79540835b0d5201093dbd8dab8 --- ...oid_inputmethod_keyboard_ProximityInfo.cpp | 42 ++++---------- native/jni/jni_common.h | 27 --------- native/jni/src/proximity_info.cpp | 56 ++++++++++--------- native/jni/src/proximity_info.h | 13 +++-- 4 files changed, 48 insertions(+), 90 deletions(-) diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index 0f2c2da43..a01ac3780 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -25,43 +25,21 @@ namespace latinime { static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, jstring localeJStr, jint maxProximityCharsSize, jint displayWidth, jint displayHeight, - jint gridWidth, jint gridHeight, jint mostCommonkeyWidth, jintArray proximityCharsArray, - jint keyCount, jintArray keyXCoordinateArray, jintArray keyYCoordinateArray, - jintArray keyWidthArray, jintArray keyHeightArray, jintArray keyCharCodeArray, - jfloatArray sweetSpotCenterXArray, jfloatArray sweetSpotCenterYArray, - jfloatArray sweetSpotRadiusArray) { + jint gridWidth, jint gridHeight, jint mostCommonkeyWidth, jintArray proximityChars, + jint keyCount, jintArray keyXCoordinates, jintArray keyYCoordinates, + jintArray keyWidths, jintArray keyHeights, jintArray keyCharCodes, + jfloatArray sweetSpotCenterXs, jfloatArray sweetSpotCenterYs, jfloatArray sweetSpotRadii) { const char *localeCStr = env->GetStringUTFChars(localeJStr, 0); - jint *proximityChars = env->GetIntArrayElements(proximityCharsArray, 0); - jint *keyXCoordinates = safeGetIntArrayElements(env, keyXCoordinateArray); - jint *keyYCoordinates = safeGetIntArrayElements(env, keyYCoordinateArray); - jint *keyWidths = safeGetIntArrayElements(env, keyWidthArray); - jint *keyHeights = safeGetIntArrayElements(env, keyHeightArray); - jint *keyCharCodes = safeGetIntArrayElements(env, keyCharCodeArray); - jfloat *sweetSpotCenterXs = safeGetFloatArrayElements(env, sweetSpotCenterXArray); - jfloat *sweetSpotCenterYs = safeGetFloatArrayElements(env, sweetSpotCenterYArray); - jfloat *sweetSpotRadii = safeGetFloatArrayElements(env, sweetSpotRadiusArray); - ProximityInfo *proximityInfo = new ProximityInfo( - localeCStr, maxProximityCharsSize, displayWidth, displayHeight, gridWidth, gridHeight, - mostCommonkeyWidth, (const int32_t*)proximityChars, keyCount, - (const int32_t*)keyXCoordinates, (const int32_t*)keyYCoordinates, - (const int32_t*)keyWidths, (const int32_t*)keyHeights, (const int32_t*)keyCharCodes, - (const float*)sweetSpotCenterXs, (const float*)sweetSpotCenterYs, - (const float*)sweetSpotRadii); - safeReleaseFloatArrayElements(env, sweetSpotRadiusArray, sweetSpotRadii); - safeReleaseFloatArrayElements(env, sweetSpotCenterYArray, sweetSpotCenterYs); - safeReleaseFloatArrayElements(env, sweetSpotCenterXArray, sweetSpotCenterXs); - safeReleaseIntArrayElements(env, keyCharCodeArray, keyCharCodes); - safeReleaseIntArrayElements(env, keyHeightArray, keyHeights); - safeReleaseIntArrayElements(env, keyWidthArray, keyWidths); - safeReleaseIntArrayElements(env, keyYCoordinateArray, keyYCoordinates); - safeReleaseIntArrayElements(env, keyXCoordinateArray, keyXCoordinates); - env->ReleaseIntArrayElements(proximityCharsArray, proximityChars, 0); + ProximityInfo *proximityInfo = new ProximityInfo(env, localeCStr, maxProximityCharsSize, + displayWidth, displayHeight, gridWidth, gridHeight, mostCommonkeyWidth, proximityChars, + keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes, + sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); env->ReleaseStringUTFChars(localeJStr, localeCStr); - return (jlong)proximityInfo; + return reinterpret_cast(proximityInfo); } static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximityInfo) { - ProximityInfo *pi = (ProximityInfo*)proximityInfo; + ProximityInfo *pi = reinterpret_cast(proximityInfo); if (!pi) return; delete pi; } diff --git a/native/jni/jni_common.h b/native/jni/jni_common.h index 658ff18b9..993f97e80 100644 --- a/native/jni/jni_common.h +++ b/native/jni/jni_common.h @@ -24,32 +24,5 @@ namespace latinime { int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); -inline jint *safeGetIntArrayElements(JNIEnv *env, jintArray jArray) { - if (jArray) { - return env->GetIntArrayElements(jArray, 0); - } else { - return 0; - } -} - -inline jfloat *safeGetFloatArrayElements(JNIEnv *env, jfloatArray jArray) { - if (jArray) { - return env->GetFloatArrayElements(jArray, 0); - } else { - return 0; - } -} - -inline void safeReleaseIntArrayElements(JNIEnv *env, jintArray jArray, jint *cArray) { - if (jArray) { - env->ReleaseIntArrayElements(jArray, cArray, 0); - } -} - -inline void safeReleaseFloatArrayElements(JNIEnv *env, jfloatArray jArray, jfloat *cArray) { - if (jArray) { - env->ReleaseFloatArrayElements(jArray, cArray, 0); - } -} } // namespace latinime #endif // LATINIME_JNI_COMMON_H diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index e9cb14e63..7bae41362 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -24,25 +24,36 @@ #include "additional_proximity_chars.h" #include "char_utils.h" #include "defines.h" +#include "jni.h" #include "proximity_info.h" namespace latinime { -inline void copyOrFillZero(void *to, const void *from, size_t size) { - if (from) { - memcpy(to, from, size); - } else { - memset(to, 0, size); +static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len, + jint *buffer) { + if (jArray && buffer) { + env->GetIntArrayRegion(jArray, 0, len, buffer); + } else if (buffer) { + memset(buffer, 0, len); } } -ProximityInfo::ProximityInfo(const char *localeCStr, const int maxProximityCharsSize, +static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jArray, jsize len, + jfloat *buffer) { + if (jArray && buffer) { + env->GetFloatArrayRegion(jArray, 0, len, buffer); + } else if (buffer) { + memset(buffer, 0, len); + } +} + +ProximityInfo::ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, - const int gridHeight, const int mostCommonKeyWidth, - const int32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, - const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights, - const int32_t *keyCharCodes, const float *sweetSpotCenterXs, const float *sweetSpotCenterYs, - const float *sweetSpotRadii) + const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars, + const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates, + const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes, + const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs, + const jfloatArray sweetSpotRadii) : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), @@ -58,20 +69,15 @@ ProximityInfo::ProximityInfo(const char *localeCStr, const int maxProximityChars AKLOGI("Create proximity info array %d", proximityGridLength); } mProximityCharsArray = new int32_t[proximityGridLength]; - memcpy(mProximityCharsArray, proximityCharsArray, - proximityGridLength * sizeof(mProximityCharsArray[0])); - - copyOrFillZero(mKeyXCoordinates, keyXCoordinates, KEY_COUNT * sizeof(mKeyXCoordinates[0])); - copyOrFillZero(mKeyYCoordinates, keyYCoordinates, KEY_COUNT * sizeof(mKeyYCoordinates[0])); - copyOrFillZero(mKeyWidths, keyWidths, KEY_COUNT * sizeof(mKeyWidths[0])); - copyOrFillZero(mKeyHeights, keyHeights, KEY_COUNT * sizeof(mKeyHeights[0])); - copyOrFillZero(mKeyCharCodes, keyCharCodes, KEY_COUNT * sizeof(mKeyCharCodes[0])); - copyOrFillZero(mSweetSpotCenterXs, sweetSpotCenterXs, - KEY_COUNT * sizeof(mSweetSpotCenterXs[0])); - copyOrFillZero(mSweetSpotCenterYs, sweetSpotCenterYs, - KEY_COUNT * sizeof(mSweetSpotCenterYs[0])); - copyOrFillZero(mSweetSpotRadii, sweetSpotRadii, KEY_COUNT * sizeof(mSweetSpotRadii[0])); - + safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray); + safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); + safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates); + safeGetOrFillZeroIntArrayRegion(env, keyWidths, KEY_COUNT, mKeyWidths); + safeGetOrFillZeroIntArrayRegion(env, keyHeights, KEY_COUNT, mKeyHeights); + safeGetOrFillZeroIntArrayRegion(env, keyCharCodes, KEY_COUNT, mKeyCharCodes); + safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterXs, KEY_COUNT, mSweetSpotCenterXs); + safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs); + safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii); initializeCodeToKeyIndex(); } diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 6c5c6bf99..5dd378d21 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -21,6 +21,7 @@ #include #include "defines.h" +#include "jni.h" namespace latinime { @@ -28,13 +29,13 @@ class Correction; class ProximityInfo { public: - ProximityInfo(const char *localeCStr, const int maxProximityCharsSize, + ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, - const int gridHeight, const int mostCommonkeyWidth, - const int32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, - const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights, - const int32_t *keyCharCodes, const float *sweetSpotCenterXs, - const float *sweetSpotCenterYs, const float *sweetSpotRadii); + const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars, + const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates, + const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes, + const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs, + const jfloatArray sweetSpotRadii); ~ProximityInfo(); bool hasSpaceProximity(const int x, const int y) const; int getNormalizedSquaredDistance(const int inputIndex, const int proximityIndex) const; From 9127811493b9288a0ca385e75db68520b197dea2 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Wed, 8 Aug 2012 21:23:25 +0900 Subject: [PATCH 009/133] Pass traverse session Change-Id: I84bfaaa406e3f51421c08a7ee8f9576ebdba54d4 --- .../inputmethod/latin/BinaryDictionary.java | 9 ++++---- .../inputmethod/latin/DicTraverseSession.java | 4 ++++ ...oid_inputmethod_latin_BinaryDictionary.cpp | 21 ++++++++++--------- ...d_inputmethod_latin_DicTraverseSession.cpp | 9 ++++---- ...oid_inputmethod_latin_DicTraverseSession.h | 4 ++-- native/jni/src/dictionary.cpp | 9 ++++---- native/jni/src/dictionary.h | 6 +++--- .../jni/src/gesture/gesture_decoder_wrapper.h | 8 +++---- .../gesture/incremental_decoder_interface.h | 7 ++++--- .../src/gesture/incremental_decoder_wrapper.h | 8 +++---- 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 7e6c53e2f..e0adc9a71 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -90,9 +90,9 @@ public class BinaryDictionary extends Dictionary { private native void closeNative(long dict); private native int getFrequencyNative(long dict, int[] word, int wordLength); private native boolean isValidBigramNative(long dict, int[] word1, int[] word2); - private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates, - int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize, - int commitPoint, boolean isGesture, + private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession, + int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, + int[] inputCodes, int codesSize, int commitPoint, boolean isGesture, int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars, int[] outputScores, int[] outputIndices, int[] outputTypes); private static native float calcNormalizedScoreNative( @@ -129,7 +129,8 @@ public class BinaryDictionary extends Dictionary { final int codesSize = isGesture ? ips.getPointerSize() : composerSize; // proximityInfo and/or prevWordForBigrams may not be null. final int tmpCount = getSuggestionsNative(mNativeDict, - proximityInfo.getNativeProximityInfo(), ips.getXCoordinates(), + proximityInfo.getNativeProximityInfo(), + mDicTraverseSession.getSession(), ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodes, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray, mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index d0c70f3ad..ae68e69dd 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -30,6 +30,10 @@ public class DicTraverseSession { locale != null ? locale.toString() : ""); } + public long getSession() { + return mNativeDicTraverseSession; + } + private native long setDicTraverseSessionNative(String locale); private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 776f5f78f..9de087a18 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -122,14 +122,15 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, } static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jlong dict, - jlong proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray, - jintArray timesArray, jintArray pointerIdArray, jintArray inputArray, jint arraySize, - jint commitPoint, jboolean isGesture, + jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, + jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdArray, + jintArray inputArray, jint arraySize, jint commitPoint, jboolean isGesture, jintArray prevWordForBigrams, jboolean useFullEditDistance, jcharArray outputArray, jintArray frequencyArray, jintArray spaceIndexArray, jintArray outputTypesArray) { - Dictionary *dictionary = (Dictionary*) dict; + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return 0; - ProximityInfo *pInfo = (ProximityInfo*)proximityInfo; + ProximityInfo *pInfo = reinterpret_cast(proximityInfo); + void *traverseSession = reinterpret_cast(dicTraverseSession); int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, 0); int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, 0); int *times = env->GetIntArrayElements(timesArray, 0); @@ -145,10 +146,10 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, int count; if (isGesture || arraySize > 1) { - count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, times, pointerIds, - inputCodes, arraySize, prevWordChars, prevWordLength, commitPoint, isGesture, - useFullEditDistance, (unsigned short*) outputChars, frequencies, spaceIndices, - outputTypes); + count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, + times, pointerIds, inputCodes, arraySize, prevWordChars, prevWordLength, + commitPoint, isGesture, useFullEditDistance, (unsigned short*) outputChars, + frequencies, spaceIndices, outputTypes); } else { count = dictionary->getBigrams(prevWordChars, prevWordLength, inputCodes, arraySize, (unsigned short*) outputChars, frequencies, outputTypes); @@ -247,7 +248,7 @@ void releaseDictBuf(void *dictBuf, const size_t length, int fd) { static JNINativeMethod sMethods[] = { {"openNative", "(Ljava/lang/String;JJIIIII)J", (void*)latinime_BinaryDictionary_open}, {"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close}, - {"getSuggestionsNative", "(JJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I", + {"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I", (void*) latinime_BinaryDictionary_getSuggestions}, {"getFrequencyNative", "(J[II)I", (void*)latinime_BinaryDictionary_getFrequency}, {"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram}, diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 8ee0450e4..e7965377c 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -26,12 +26,13 @@ void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring localejStr) { - void *session = DicTraverseWrapper::getDicTraverseSession(); - return reinterpret_cast(session); + void *traverseSession = DicTraverseWrapper::getDicTraverseSession(); + return reinterpret_cast(traverseSession); } -static void latinime_DicTraverseSession_release(JNIEnv *env, jobject object, jlong session) { - void *pi = reinterpret_cast(session); +static void latinime_DicTraverseSession_release( + JNIEnv *env, jobject object, jlong traverseSession) { + void *pi = reinterpret_cast(traverseSession); if (!pi) return; DicTraverseWrapper::releaseDicTraverseSession(pi); } diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h index 5238fd0af..a84fe78cc 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h @@ -31,9 +31,9 @@ class DicTraverseWrapper { } return 0; } - static void releaseDicTraverseSession(void *session) { + static void releaseDicTraverseSession(void *traverseSession) { if (sDicTraverseSessionReleaseMethod) { - sDicTraverseSessionReleaseMethod(session); + sDicTraverseSessionReleaseMethod(traverseSession); } } private: diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index ee55cfa60..8c785a263 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -56,16 +56,17 @@ Dictionary::~Dictionary() { delete mGestureDecoder; } -int Dictionary::getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates, - int *times, int *pointerIds, int *codes, int codesSize, int *prevWordChars, +int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, + int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, + int *codes, int codesSize, int *prevWordChars, int prevWordLength, int commitPoint, bool isGesture, bool useFullEditDistance, unsigned short *outWords, int *frequencies, int *spaceIndices, int *outputTypes) { int result = 0; if (isGesture) { mGestureDecoder->setPrevWord(prevWordChars, prevWordLength); - result = mGestureDecoder->getSuggestions(proximityInfo, xcoordinates, ycoordinates, - times, pointerIds, codes, codesSize, commitPoint, + result = mGestureDecoder->getSuggestions(proximityInfo, traverseSession, + xcoordinates, ycoordinates, times, pointerIds, codes, codesSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes); if (DEBUG_DICT) { DUMP_RESULT(outWords, frequencies, 18 /* MAX_WORDS */, MAX_WORD_LENGTH_INTERNAL); diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h index ab238c824..2c79527be 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/dictionary.h @@ -44,9 +44,9 @@ class Dictionary { Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler, int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions); - int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates, - int *times, int *pointerIds, int *codes, int codesSize, int *prevWordChars, - int prevWordLength, int commitPoint, bool isGesture, + int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates, + int *ycoordinates, int *times, int *pointerIds, int *codes, int codesSize, + int *prevWordChars, int prevWordLength, int commitPoint, bool isGesture, bool useFullEditDistance, unsigned short *outWords, int *frequencies, int *spaceIndices, int *outputTypes); diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index 03c84b5fd..b70c8e0a3 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -37,15 +37,15 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { delete mIncrementalDecoderInterface; } - int getSuggestions(ProximityInfo *pInfo, int *inputXs, int *inputYs, int *times, - int *pointerIds, int *codes, int inputSize, int commitPoint, + int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, + int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) { if (!mIncrementalDecoderInterface) { return 0; } return mIncrementalDecoderInterface->getSuggestions( - pInfo, inputXs, inputYs, times, pointerIds, codes, inputSize, commitPoint, - outWords, frequencies, outputIndices, outputTypes); + pInfo, traverseSession, inputXs, inputYs, times, pointerIds, codes, + inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } void reset() { diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index 6d2e273da..e8d3a5333 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -28,9 +28,10 @@ class ProximityInfo; class IncrementalDecoderInterface { public: - virtual int getSuggestions(ProximityInfo *pInfo, int *inputXs, int *inputYs, int *times, - int *pointerIds, int *codes, int inputSize, int commitPoint, - unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) = 0; + virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession, + int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, + int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, + int *outputIndices, int *outputTypes) = 0; virtual void reset() = 0; virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) = 0; diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 698061548..8d66b4ec1 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -37,15 +37,15 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { delete mIncrementalDecoderInterface; } - int getSuggestions(ProximityInfo *pInfo, int *inputXs, int *inputYs, int *times, - int *pointerIds, int *codes, int inputSize, int commitPoint, + int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, + int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) { if (!mIncrementalDecoderInterface) { return 0; } return mIncrementalDecoderInterface->getSuggestions( - pInfo, inputXs, inputYs, times, pointerIds, codes, inputSize, commitPoint, - outWords, frequencies, outputIndices, outputTypes); + pInfo, traverseSession, inputXs, inputYs, times, pointerIds, codes, + inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } void reset() { From 8335c59ea7715f3dbc6625f128a7a038f314a89f Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 8 Aug 2012 11:41:58 +0900 Subject: [PATCH 010/133] Fix NPE and don't call UI API from non-UI thread This change also reverts I29ec3e9f10484f30288fac76f0c45b3707bc2259 Bug: 6956969 Bug: 6891020 Bug: 6947253 Change-Id: I7901c0f9489b4deb79314c2c903b138611bfd78e --- .../inputmethod/keyboard/KeyDetector.java | 3 ++ .../inputmethod/keyboard/KeyboardView.java | 8 ++--- .../keyboard/MainKeyboardView.java | 15 ++++---- .../keyboard/MoreKeysDetector.java | 6 +--- .../inputmethod/keyboard/PointerTracker.java | 34 +++++++++++++------ .../android/inputmethod/latin/LatinIME.java | 22 +++++------- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 97d88af4a..c0e6aa8d7 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -59,6 +59,9 @@ public class KeyDetector { } public Keyboard getKeyboard() { + if (mKeyboard == null) { + throw new IllegalStateException("keyboard isn't set"); + } return mKeyboard; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 0e6de7032..b8eb9ec96 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -108,9 +108,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private int mDelayAfterPreview; private final PreviewPlacerView mPreviewPlacerView; - /** True if {@link KeyboardView} should handle gesture events. */ - protected boolean mShouldHandleGesture; - // Drawing /** True if the entire keyboard needs to be dimmed. */ private boolean mNeedsToDimEntireKeyboard; @@ -438,9 +435,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { return mShowKeyPreviewPopup; } - public void setGestureHandlingMode(boolean shouldHandleGesture, - boolean drawsGesturePreviewTrail, boolean drawsGestureFloatingPreviewText) { - mShouldHandleGesture = shouldHandleGesture; + public void setGesturePreviewMode(boolean drawsGesturePreviewTrail, + boolean drawsGestureFloatingPreviewText) { mPreviewPlacerView.setGesturePreviewMode( drawsGesturePreviewTrail, drawsGestureFloatingPreviewText); } diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index fe9cb9415..4088f3e93 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -482,7 +482,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); - PointerTracker.setKeyDetector(mKeyDetector, mShouldHandleGesture); + PointerTracker.setKeyDetector(mKeyDetector); mTouchScreenRegulator.setKeyboard(keyboard); mMoreKeysPanelCache.clear(); @@ -500,12 +500,13 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard); } - @Override - public void setGestureHandlingMode(final boolean shouldHandleGesture, - boolean drawsGesturePreviewTrail, boolean drawsGestureFloatingPreviewText) { - super.setGestureHandlingMode(shouldHandleGesture, drawsGesturePreviewTrail, - drawsGestureFloatingPreviewText); - PointerTracker.setKeyDetector(mKeyDetector, shouldHandleGesture); + // Note that this method is called from a non-UI thread. + public void setMainDictionaryAvailability(boolean mainDictionaryAvailable) { + PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable); + } + + public void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) { + PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser); } /** diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java index a183546dd..cd4e3001e 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java @@ -39,11 +39,7 @@ public class MoreKeysDetector extends KeyDetector { Key nearestKey = null; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; - final Keyboard keyboard = getKeyboard(); - if (keyboard == null) { - throw new NullPointerException("Keyboard isn't set"); - } - for (final Key key : keyboard.mKeys) { + for (final Key key : getKeyboard().mKeys) { final int dist = key.squaredDistanceToEdge(touchX, touchY); if (dist < nearestDist) { nearestKey = key; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 2b90a3cb9..85360c410 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -29,6 +29,7 @@ import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; @@ -43,6 +44,9 @@ public class PointerTracker implements PointerTrackerQueue.Element { /** True if {@link PointerTracker}s should handle gesture events. */ private static boolean sShouldHandleGesture = false; + private static boolean sMainDictionaryAvailable = false; + private static boolean sGestureHandlingEnabledByInputField = false; + private static boolean sGestureHandlingEnabledByUser = false; private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec @@ -198,7 +202,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT); - updateGestureHandlingMode(null, false /* shouldHandleGesture */); } public static void setParameters(MainKeyboardView.PointerTrackerParams params) { @@ -207,14 +210,22 @@ public class PointerTracker implements PointerTrackerQueue.Element { params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); } - private static void updateGestureHandlingMode(Keyboard keyboard, boolean shouldHandleGesture) { - if (!shouldHandleGesture - || AccessibilityUtils.getInstance().isTouchExplorationEnabled() - || (keyboard != null && keyboard.mId.passwordInput())) { - sShouldHandleGesture = false; - } else { - sShouldHandleGesture = true; - } + private static void updateGestureHandlingMode() { + sShouldHandleGesture = sMainDictionaryAvailable + && sGestureHandlingEnabledByInputField + && sGestureHandlingEnabledByUser + && !AccessibilityUtils.getInstance().isTouchExplorationEnabled(); + } + + // Note that this method is called from a non-UI thread. + public static void setMainDictionaryAvailability(boolean mainDictionaryAvailable) { + sMainDictionaryAvailable = mainDictionaryAvailable; + updateGestureHandlingMode(); + } + + public static void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) { + sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser; + updateGestureHandlingMode(); } public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) { @@ -241,7 +252,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public static void setKeyDetector(KeyDetector keyDetector, boolean shouldHandleGesture) { + public static void setKeyDetector(KeyDetector keyDetector) { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); @@ -250,7 +261,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { tracker.mKeyboardLayoutHasBeenChanged = true; } final Keyboard keyboard = keyDetector.getKeyboard(); - updateGestureHandlingMode(keyboard, shouldHandleGesture); + sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput(); + updateGestureHandlingMode(); } public static void dismissAllKeyPreviews() { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 43901bab4..894b69b79 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -433,10 +433,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary()); } + // Note that this method is called from a non-UI thread. @Override public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable) { mIsMainDictionaryAvailable = isMainDictionaryAvailable; - updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); + final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + if (mainKeyboardView != null) { + mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable); + } } private void initSuggest() { @@ -701,7 +705,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } switcher.loadKeyboard(editorInfo, mCurrentSettings); - updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); } setSuggestionStripShownInternal( isSuggestionsStripVisible(), /* needsInputViewShown */ false); @@ -721,6 +724,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn, mCurrentSettings.mKeyPreviewPopupDismissDelay); + mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled); + mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled, + mCurrentSettings.mGestureFloatingPreviewTextEnabled); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } @@ -2103,7 +2109,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mKeyboardSwitcher.getMainKeyboardView() != null) { // Reload keyboard because the current language has been changed. mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings); - updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability(); } // Since we just changed languages, we should re-evaluate suggestions with whatever word // we are currently composing. If we are not composing anything, we may want to display @@ -2111,17 +2116,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } - private void updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability() { - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView != null) { - final boolean shouldHandleGesture = mCurrentSettings.mGestureInputEnabled - && mIsMainDictionaryAvailable; - mainKeyboardView.setGestureHandlingMode(shouldHandleGesture, - mCurrentSettings.mGesturePreviewTrailEnabled, - mCurrentSettings.mGestureFloatingPreviewTextEnabled); - } - } - // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to // {@link KeyboardSwitcher}. Called from KeyboardSwitcher public void hapticAndAudioFeedback(final int primaryCode) { From ccd8ed53b33a5f91961f40d576abd0a91b87a66d Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 9 Aug 2012 13:33:16 +0900 Subject: [PATCH 011/133] Support stdin and stdout for compress/uncompress This will improve flexibility in dictionary generation by allowing pipelined commands. Change-Id: Ia07bdc82c8bd740b5dfee6b5fd1a067f4b8b7ba5 --- .../inputmethod/latin/dicttool/Compress.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java index a76ec50e0..3cb0a12c4 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java @@ -46,46 +46,52 @@ public class Compress { static public class Compressor extends Dicttool.Command { public static final String COMMAND = "compress"; - private static final String SUFFIX = ".compressed"; + public static final String STDIN_OR_STDOUT = "-"; public Compressor() { } public String getHelp() { - return "compress : Compresses a file using gzip compression"; + return COMMAND + " : " + + "Compresses a file using gzip compression"; } public void run() throws IOException { - if (mArgs.length < 1) { - throw new RuntimeException("Not enough arguments for command " + COMMAND); + if (mArgs.length > 2) { + throw new RuntimeException("Too many arguments for command " + COMMAND); } - final String inFilename = mArgs[0]; - final String outFilename = inFilename + SUFFIX; - final FileInputStream input = new FileInputStream(new File(inFilename)); - final FileOutputStream output = new FileOutputStream(new File(outFilename)); + final String inFilename = mArgs.length >= 1 ? mArgs[0] : STDIN_OR_STDOUT; + final String outFilename = mArgs.length >= 2 ? mArgs[1] : STDIN_OR_STDOUT; + final InputStream input = inFilename.equals(STDIN_OR_STDOUT) ? System.in + : new FileInputStream(new File(inFilename)); + final OutputStream output = outFilename.equals(STDIN_OR_STDOUT) ? System.out + : new FileOutputStream(new File(outFilename)); copy(input, new GZIPOutputStream(output)); } } static public class Uncompressor extends Dicttool.Command { public static final String COMMAND = "uncompress"; - private static final String SUFFIX = ".uncompressed"; + public static final String STDIN_OR_STDOUT = "-"; public Uncompressor() { } public String getHelp() { - return "uncompress : Uncompresses a file compressed with gzip compression"; + return COMMAND + " : " + + "Uncompresses a file compressed with gzip compression"; } public void run() throws IOException { - if (mArgs.length < 1) { - throw new RuntimeException("Not enough arguments for command " + COMMAND); + if (mArgs.length > 2) { + throw new RuntimeException("Too many arguments for command " + COMMAND); } - final String inFilename = mArgs[0]; - final String outFilename = inFilename + SUFFIX; - final FileInputStream input = new FileInputStream(new File(inFilename)); - final FileOutputStream output = new FileOutputStream(new File(outFilename)); + final String inFilename = mArgs.length >= 1 ? mArgs[0] : STDIN_OR_STDOUT; + final String outFilename = mArgs.length >= 2 ? mArgs[1] : STDIN_OR_STDOUT; + final InputStream input = inFilename.equals(STDIN_OR_STDOUT) ? System.in + : new FileInputStream(new File(inFilename)); + final OutputStream output = outFilename.equals(STDIN_OR_STDOUT) ? System.out + : new FileOutputStream(new File(outFilename)); copy(new GZIPInputStream(input), output); } } From 015114592460cca82e5196f2c2e2eff23f9d97ae Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Thu, 9 Aug 2012 15:58:15 +0900 Subject: [PATCH 012/133] Use GetStringUTFRegion Change-Id: I56ad66af4ee42ed32a6fd909a9a34d601874783a --- ...android_inputmethod_keyboard_ProximityInfo.cpp | 5 +---- native/jni/src/proximity_info.cpp | 15 ++++++++++----- native/jni/src/proximity_info.h | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index a01ac3780..f9b23f06d 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -29,18 +29,15 @@ static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, jint keyCount, jintArray keyXCoordinates, jintArray keyYCoordinates, jintArray keyWidths, jintArray keyHeights, jintArray keyCharCodes, jfloatArray sweetSpotCenterXs, jfloatArray sweetSpotCenterYs, jfloatArray sweetSpotRadii) { - const char *localeCStr = env->GetStringUTFChars(localeJStr, 0); - ProximityInfo *proximityInfo = new ProximityInfo(env, localeCStr, maxProximityCharsSize, + ProximityInfo *proximityInfo = new ProximityInfo(env, localeJStr, maxProximityCharsSize, displayWidth, displayHeight, gridWidth, gridHeight, mostCommonkeyWidth, proximityChars, keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); - env->ReleaseStringUTFChars(localeJStr, localeCStr); return reinterpret_cast(proximityInfo); } static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximityInfo) { ProximityInfo *pi = reinterpret_cast(proximityInfo); - if (!pi) return; delete pi; } diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 7bae41362..4f6507e53 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -47,7 +47,7 @@ static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jA } } -ProximityInfo::ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxProximityCharsSize, +ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates, @@ -62,12 +62,16 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxP KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs - && sweetSpotCenterYs && sweetSpotRadii), - mLocaleStr(localeCStr) { + && sweetSpotCenterYs && sweetSpotRadii) { const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; if (DEBUG_PROXIMITY_INFO) { AKLOGI("Create proximity info array %d", proximityGridLength); } + const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr); + char localeCStr[localeCStrUtf8Length + 1]; + env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), localeCStr); + localeCStr[localeCStrUtf8Length] = '\0'; + mLocaleStr = new std::string(localeCStr); mProximityCharsArray = new int32_t[proximityGridLength]; safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray); safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); @@ -94,6 +98,7 @@ void ProximityInfo::initializeCodeToKeyIndex() { } ProximityInfo::~ProximityInfo() { + delete mLocaleStr; delete[] mProximityCharsArray; } @@ -166,7 +171,7 @@ void ProximityInfo::calculateNearbyKeyCodes( } } const int additionalProximitySize = - AdditionalProximityChars::getAdditionalCharsSize(&mLocaleStr, primaryKey); + AdditionalProximityChars::getAdditionalCharsSize(mLocaleStr, primaryKey); if (additionalProximitySize > 0) { inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE; if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) { @@ -177,7 +182,7 @@ void ProximityInfo::calculateNearbyKeyCodes( } const int32_t *additionalProximityChars = - AdditionalProximityChars::getAdditionalChars(&mLocaleStr, primaryKey); + AdditionalProximityChars::getAdditionalChars(mLocaleStr, primaryKey); for (int j = 0; j < additionalProximitySize; ++j) { const int32_t ac = additionalProximityChars[j]; int k = 0; diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 5dd378d21..7222d0d5d 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -29,7 +29,7 @@ class Correction; class ProximityInfo { public: - ProximityInfo(JNIEnv *env, const char *localeCStr, const int maxProximityCharsSize, + ProximityInfo(JNIEnv *env, const jstring localeJStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, const int gridHeight, const int mostCommonKeyWidth, const jintArray proximityChars, const int keyCount, const jintArray keyXCoordinates, const jintArray keyYCoordinates, @@ -76,7 +76,7 @@ class ProximityInfo { } std::string getLocaleStr() const { - return mLocaleStr; + return *mLocaleStr; } int getKeyCount() const { @@ -129,7 +129,7 @@ class ProximityInfo { const int CELL_HEIGHT; const int KEY_COUNT; const bool HAS_TOUCH_POSITION_CORRECTION_DATA; - const std::string mLocaleStr; + const std::string *mLocaleStr; int32_t *mProximityCharsArray; int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; From 2be2d776649c9c0b3914135794ab7a7e92e753f9 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 9 Aug 2012 16:09:54 +0900 Subject: [PATCH 013/133] Update dictionary availability at onStartInputView This is a follow up of I7901c0f9489b4deb79314c2c903b138611bfd78e Change-Id: I38c22e886db247c402a964e3ddd10b09a461151c --- java/src/com/android/inputmethod/latin/LatinIME.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 894b69b79..e0dbb8e6e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -722,6 +722,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacesTimer(); + mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable); mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn, mCurrentSettings.mKeyPreviewPopupDismissDelay); mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled); From 877157e1f5ed44d6490eaf8fab430f9a73caf34d Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 9 Aug 2012 15:25:01 +0900 Subject: [PATCH 014/133] Optimize keyboard drawing code a bit * Remove useless boolean flag. * Use invalidate(int,int,int,int). * Don't draw background when hardware acceleration is enabled. Change-Id: Id560ac84f33b5151437a42a4ff22736284d71e2d --- .../inputmethod/keyboard/KeyboardView.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index b8eb9ec96..fcf97b99c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -111,8 +111,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Drawing /** True if the entire keyboard needs to be dimmed. */ private boolean mNeedsToDimEntireKeyboard; - /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/ - private boolean mBufferNeedsUpdate; /** True if all keys should be drawn */ private boolean mInvalidateAllKeys; /** The keys that should be drawn */ @@ -459,8 +457,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { onDrawKeyboard(canvas); return; } - if (mBufferNeedsUpdate || mOffscreenBuffer == null) { - mBufferNeedsUpdate = false; + + final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty(); + if (bufferNeedsUpdates || mOffscreenBuffer == null) { if (maybeAllocateOffscreenBuffer()) { mInvalidateAllKeys = true; // TODO: Stop using the offscreen canvas even when in software rendering @@ -524,13 +523,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } if (!isHardwareAccelerated) { canvas.clipRegion(mClipRegion, Region.Op.REPLACE); - } - - // Draw keyboard background. - canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); - final Drawable background = getBackground(); - if (background != null) { - background.draw(canvas); + // Draw keyboard background. + canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); + final Drawable background = getBackground(); + if (background != null) { + background.draw(canvas); + } } // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. @@ -1048,7 +1046,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void invalidateAllKeys() { mInvalidatedKeys.clear(); mInvalidateAllKeys = true; - mBufferNeedsUpdate = true; invalidate(); } @@ -1066,9 +1063,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mInvalidatedKeys.add(key); final int x = key.mX + getPaddingLeft(); final int y = key.mY + getPaddingTop(); - mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); - mBufferNeedsUpdate = true; - invalidate(mWorkingRect); + invalidate(x, y, x + key.mWidth, y + key.mHeight); } public void closing() { From 9d29871605515ac0f6071882213a90bc75dfd9ba Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Thu, 9 Aug 2012 12:42:12 +0900 Subject: [PATCH 015/133] Add reset method for dic travserse session Change-Id: Ia3dd924f92549da2cf8302ef77c629902d4f275b --- .../inputmethod/latin/DicTraverseSession.java | 14 ++++++++++++-- ...ndroid_inputmethod_latin_DicTraverseSession.cpp | 14 +++++++++++--- ..._android_inputmethod_latin_DicTraverseSession.h | 7 +++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index ae68e69dd..437876e05 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -22,20 +22,30 @@ public class DicTraverseSession { static { JniUtils.loadNativeLibrary(); } + private native long setDicTraverseSessionNative(String locale); + private native void initDicTraverseSessionNative( + long nativeDicTraverseSession, int[] previousWord, int previwousWordLength); + private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); private long mNativeDicTraverseSession; public DicTraverseSession(Locale locale) { mNativeDicTraverseSession = createNativeDicTraverseSession( locale != null ? locale.toString() : ""); + initSession(); } public long getSession() { return mNativeDicTraverseSession; } - private native long setDicTraverseSessionNative(String locale); - private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); + public void initSession() { + initSession(null, 0); + } + + public void initSession(int[] previousWord, int previousWordLength) { + initDicTraverseSessionNative(mNativeDicTraverseSession, previousWord, previousWordLength); + } private final long createNativeDicTraverseSession(String locale) { return setDicTraverseSessionNative(locale); diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index e7965377c..0f5c39642 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -23,6 +23,8 @@ namespace latinime { void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)() = 0; void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; +void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)( + JNIEnv *, void *, const jintArray, const jint) = 0; static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring localejStr) { @@ -30,15 +32,21 @@ static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, return reinterpret_cast(traverseSession); } +static void latinime_initDicTraverseSession(JNIEnv *env, jlong traverseSession, + jintArray previousWord, jint previousWordLength) { + void *ts = reinterpret_cast(traverseSession); + DicTraverseWrapper::initDicTraverseSession(env, ts, previousWord, previousWordLength); +} + static void latinime_DicTraverseSession_release( JNIEnv *env, jobject object, jlong traverseSession) { - void *pi = reinterpret_cast(traverseSession); - if (!pi) return; - DicTraverseWrapper::releaseDicTraverseSession(pi); + void *ts = reinterpret_cast(traverseSession); + DicTraverseWrapper::releaseDicTraverseSession(ts); } static JNINativeMethod sMethods[] = { {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, + {"initDicTraverseSessionNative", "(J[II)V", (void*)latinime_initDicTraverseSession}, {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_DicTraverseSession_release} }; diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h index a84fe78cc..a76815d7f 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h @@ -31,6 +31,12 @@ class DicTraverseWrapper { } return 0; } + static void initDicTraverseSession(JNIEnv *env, void *traverseSession, + const jintArray prevWord, const jint prevWordLength) { + if (sDicTraverseSessionInitMethod) { + sDicTraverseSessionInitMethod(env, traverseSession, prevWord, prevWordLength); + } + } static void releaseDicTraverseSession(void *traverseSession) { if (sDicTraverseSessionReleaseMethod) { sDicTraverseSessionReleaseMethod(traverseSession); @@ -39,6 +45,7 @@ class DicTraverseWrapper { private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); static void *(*sDicTraverseSessionFactoryMethod)(); + static void (*sDicTraverseSessionInitMethod)(JNIEnv *, void *, const jintArray, const jint); static void (*sDicTraverseSessionReleaseMethod)(void *); }; int register_DicTraverseSession(JNIEnv *env); From 805ac949d22946e60cbe311676e32d1f4f02ced3 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Thu, 9 Aug 2012 19:32:03 +0900 Subject: [PATCH 016/133] Fix string.compare() logic. It happens to be working luckily. Change-Id: I262c6d5779124e5b09ad1a66ca3c9ddb2fc6d7d5 --- native/jni/src/additional_proximity_chars.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/native/jni/src/additional_proximity_chars.h b/native/jni/src/additional_proximity_chars.h index ba76cfced..c22de7d3d 100644 --- a/native/jni/src/additional_proximity_chars.h +++ b/native/jni/src/additional_proximity_chars.h @@ -40,8 +40,9 @@ class AdditionalProximityChars { static const int32_t EN_US_ADDITIONAL_U[]; static bool isEnLocale(const std::string *locale_str) { - return locale_str && locale_str->size() >= LOCALE_EN_US.size() - && LOCALE_EN_US.compare(0, LOCALE_EN_US.size(), *locale_str); + const size_t LOCALE_EN_US_SIZE = LOCALE_EN_US.size(); + return locale_str && locale_str->size() >= LOCALE_EN_US_SIZE + && locale_str->compare(0, LOCALE_EN_US_SIZE, LOCALE_EN_US) == 0; } public: @@ -84,10 +85,6 @@ class AdditionalProximityChars { return 0; } } - - static bool hasAdditionalChars(const std::string *locale_str, const int32_t c) { - return getAdditionalCharsSize(locale_str, c) > 0; - } }; } // namespace latinime #endif // LATINIME_ADDITIONAL_PROXIMITY_CHARS_H From 9e0c711a6230af1db0753af401804c95e4bee69d Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Thu, 9 Aug 2012 22:26:58 +0900 Subject: [PATCH 017/133] Stop using STL string in additional_proximity_chars Change-Id: Ic016fd5983b8855b0fd9506a17b205db86af3a2f --- native/jni/src/additional_proximity_chars.cpp | 4 +++- native/jni/src/additional_proximity_chars.h | 20 +++++++++---------- native/jni/src/defines.h | 3 +++ native/jni/src/proximity_info.cpp | 12 +++++------ native/jni/src/proximity_info.h | 7 +++---- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/native/jni/src/additional_proximity_chars.cpp b/native/jni/src/additional_proximity_chars.cpp index de8764678..f59492741 100644 --- a/native/jni/src/additional_proximity_chars.cpp +++ b/native/jni/src/additional_proximity_chars.cpp @@ -17,7 +17,9 @@ #include "additional_proximity_chars.h" namespace latinime { -const std::string AdditionalProximityChars::LOCALE_EN_US("en"); +// TODO: Stop using hardcoded additional proximity characters. +// TODO: Have proximity character informations in each language's binary dictionary. +const char *AdditionalProximityChars::LOCALE_EN_US = "en"; const int32_t AdditionalProximityChars::EN_US_ADDITIONAL_A[EN_US_ADDITIONAL_A_SIZE] = { 'e', 'i', 'o', 'u' diff --git a/native/jni/src/additional_proximity_chars.h b/native/jni/src/additional_proximity_chars.h index c22de7d3d..1fe996d0d 100644 --- a/native/jni/src/additional_proximity_chars.h +++ b/native/jni/src/additional_proximity_chars.h @@ -17,8 +17,8 @@ #ifndef LATINIME_ADDITIONAL_PROXIMITY_CHARS_H #define LATINIME_ADDITIONAL_PROXIMITY_CHARS_H +#include #include -#include #include "defines.h" @@ -27,7 +27,7 @@ namespace latinime { class AdditionalProximityChars { private: DISALLOW_IMPLICIT_CONSTRUCTORS(AdditionalProximityChars); - static const std::string LOCALE_EN_US; + static const char *LOCALE_EN_US; static const int EN_US_ADDITIONAL_A_SIZE = 4; static const int32_t EN_US_ADDITIONAL_A[]; static const int EN_US_ADDITIONAL_E_SIZE = 4; @@ -39,15 +39,15 @@ class AdditionalProximityChars { static const int EN_US_ADDITIONAL_U_SIZE = 4; static const int32_t EN_US_ADDITIONAL_U[]; - static bool isEnLocale(const std::string *locale_str) { - const size_t LOCALE_EN_US_SIZE = LOCALE_EN_US.size(); - return locale_str && locale_str->size() >= LOCALE_EN_US_SIZE - && locale_str->compare(0, LOCALE_EN_US_SIZE, LOCALE_EN_US) == 0; + static bool isEnLocale(const char *localeStr) { + const size_t LOCALE_EN_US_SIZE = strlen(LOCALE_EN_US); + return localeStr && strlen(localeStr) >= LOCALE_EN_US_SIZE + && strncmp(localeStr, LOCALE_EN_US, LOCALE_EN_US_SIZE) == 0; } public: - static int getAdditionalCharsSize(const std::string *locale_str, const int32_t c) { - if (!isEnLocale(locale_str)) { + static int getAdditionalCharsSize(const char *localeStr, const int32_t c) { + if (!isEnLocale(localeStr)) { return 0; } switch(c) { @@ -66,8 +66,8 @@ class AdditionalProximityChars { } } - static const int32_t *getAdditionalChars(const std::string *locale_str, const int32_t c) { - if (!isEnLocale(locale_str)) { + static const int32_t *getAdditionalChars(const char *localeStr, const int32_t c) { + if (!isEnLocale(localeStr)) { return 0; } switch(c) { diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 31dd61e30..484fc6bde 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -265,6 +265,9 @@ static inline void prof_out(void) { // This must be equal to ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE in KeyDetector.java #define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2 +// Assuming locale strings such as en_US, sr-Latn etc. +#define MAX_LOCALE_STRING_LENGTH 10 + // Word limit for sub queues used in WordsPriorityQueuePool. Sub queues are temporary queues used // for better performance. // Holds up to 1 candidate for each word diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 4f6507e53..2564c8a67 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #define LOG_TAG "LatinIME: proximity_info.cpp" @@ -68,10 +67,12 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma AKLOGI("Create proximity info array %d", proximityGridLength); } const jsize localeCStrUtf8Length = env->GetStringUTFLength(localeJStr); - char localeCStr[localeCStrUtf8Length + 1]; - env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), localeCStr); - localeCStr[localeCStrUtf8Length] = '\0'; - mLocaleStr = new std::string(localeCStr); + if (localeCStrUtf8Length >= MAX_LOCALE_STRING_LENGTH) { + AKLOGI("Locale string length too long: length=%d", localeCStrUtf8Length); + assert(false); + } + memset(mLocaleStr, 0, sizeof(mLocaleStr)); + env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr); mProximityCharsArray = new int32_t[proximityGridLength]; safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray); safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); @@ -98,7 +99,6 @@ void ProximityInfo::initializeCodeToKeyIndex() { } ProximityInfo::~ProximityInfo() { - delete mLocaleStr; delete[] mProximityCharsArray; } diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 7222d0d5d..d7e24c5f5 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -18,7 +18,6 @@ #define LATINIME_PROXIMITY_INFO_H #include -#include #include "defines.h" #include "jni.h" @@ -75,8 +74,8 @@ class ProximityInfo { return MOST_COMMON_KEY_WIDTH_SQUARE; } - std::string getLocaleStr() const { - return *mLocaleStr; + const char *getLocaleStr() const { + return mLocaleStr; } int getKeyCount() const { @@ -129,7 +128,7 @@ class ProximityInfo { const int CELL_HEIGHT; const int KEY_COUNT; const bool HAS_TOUCH_POSITION_CORRECTION_DATA; - const std::string *mLocaleStr; + char mLocaleStr[MAX_LOCALE_STRING_LENGTH]; int32_t *mProximityCharsArray; int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; From 04a89132e939db174079d155e25714d7f616ca8b Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Thu, 9 Aug 2012 23:23:35 +0900 Subject: [PATCH 018/133] Use GetStringUTFRegion for filename Change-Id: I2e9ad092ccf4c23c1c6b71916c962994314d37b6 --- ...m_android_inputmethod_latin_BinaryDictionary.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 9de087a18..6e2efbec1 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -36,7 +36,7 @@ namespace latinime { class ProximityInfo; -void releaseDictBuf(void *dictBuf, const size_t length, int fd); +static void releaseDictBuf(void *dictBuf, const size_t length, int fd); static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, jstring sourceDir, jlong dictOffset, jlong dictSize, @@ -44,11 +44,14 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, jint maxPredictions) { PROF_OPEN; PROF_START(66); - const char *sourceDirChars = env->GetStringUTFChars(sourceDir, 0); - if (sourceDirChars == 0) { + const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir); + if (sourceDirUtf8Length <= 0) { AKLOGE("DICT: Can't get sourceDir string"); return 0; } + char sourceDirChars[sourceDirUtf8Length + 1]; + env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars); + sourceDirChars[sourceDirUtf8Length] = '\0'; int fd = 0; void *dictBuf = 0; int adjust = 0; @@ -98,8 +101,6 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, return 0; } #endif // USE_MMAP_FOR_DICTIONARY - env->ReleaseStringUTFChars(sourceDir, sourceDirChars); - if (!dictBuf) { AKLOGE("DICT: dictBuf is null"); return 0; @@ -230,7 +231,7 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong d delete dictionary; } -void releaseDictBuf(void *dictBuf, const size_t length, int fd) { +static void releaseDictBuf(void *dictBuf, const size_t length, int fd) { #ifdef USE_MMAP_FOR_DICTIONARY int ret = munmap(dictBuf, length); if (ret != 0) { From 5bb420627520412a621121f5c21da123f6a4cb1c Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 10 Aug 2012 00:40:34 +0900 Subject: [PATCH 019/133] Use more JNI Region calls Change-Id: I371db517ff4749c0b7e5bdb4362e6310c91a24dc --- .../inputmethod/latin/BinaryDictionary.java | 18 ++--- ...oid_inputmethod_latin_BinaryDictionary.cpp | 71 ++++++++++--------- 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index e0adc9a71..6ac6e83a3 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -88,17 +88,15 @@ public class BinaryDictionary extends Dictionary { int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions); private native void closeNative(long dict); - private native int getFrequencyNative(long dict, int[] word, int wordLength); + private native int getFrequencyNative(long dict, int[] word); private native boolean isValidBigramNative(long dict, int[] word1, int[] word2); private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, int codesSize, int commitPoint, boolean isGesture, int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars, int[] outputScores, int[] outputIndices, int[] outputTypes); - private static native float calcNormalizedScoreNative( - char[] before, int beforeLength, char[] after, int afterLength, int score); - private static native int editDistanceNative( - char[] before, int beforeLength, char[] after, int afterLength); + private static native float calcNormalizedScoreNative(char[] before, char[] after, int score); + private static native int editDistanceNative(char[] before, char[] after); private final void loadDictionary(String path, long startOffset, long length) { mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER, @@ -158,13 +156,11 @@ public class BinaryDictionary extends Dictionary { } public static float calcNormalizedScore(String before, String after, int score) { - return calcNormalizedScoreNative(before.toCharArray(), before.length(), - after.toCharArray(), after.length(), score); + return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score); } public static int editDistance(String before, String after) { - return editDistanceNative( - before.toCharArray(), before.length(), after.toCharArray(), after.length()); + return editDistanceNative(before.toCharArray(), after.toCharArray()); } @Override @@ -175,8 +171,8 @@ public class BinaryDictionary extends Dictionary { @Override public int getFrequency(CharSequence word) { if (word == null) return -1; - int[] chars = StringUtils.toCodePointArray(word.toString()); - return getFrequencyNative(mNativeDict, chars, chars.length); + int[] codePoints = StringUtils.toCodePointArray(word.toString()); + return getFrequencyNative(mNativeDict, codePoints); } // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 6e2efbec1..1596a636d 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -172,53 +172,54 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, } static jint latinime_BinaryDictionary_getFrequency(JNIEnv *env, jobject object, jlong dict, - jintArray wordArray, jint wordLength) { - Dictionary *dictionary = (Dictionary*)dict; - if (!dictionary) return (jboolean) false; - jint *word = env->GetIntArrayElements(wordArray, 0); - jint result = dictionary->getFrequency(word, wordLength); - env->ReleaseIntArrayElements(wordArray, word, JNI_ABORT); - return result; + jintArray wordArray) { + Dictionary *dictionary = reinterpret_cast(dict); + if (!dictionary) return 0; + const jsize codePointLength = env->GetArrayLength(wordArray); + int codePoints[codePointLength]; + env->GetIntArrayRegion(wordArray, 0, codePointLength, codePoints); + return dictionary->getFrequency(codePoints, codePointLength); } static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jobject object, jlong dict, jintArray wordArray1, jintArray wordArray2) { - Dictionary *dictionary = (Dictionary*)dict; + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return (jboolean) false; - jint *word1 = env->GetIntArrayElements(wordArray1, 0); - jint *word2 = env->GetIntArrayElements(wordArray2, 0); - jsize length1 = word1 ? env->GetArrayLength(wordArray1) : 0; - jsize length2 = word2 ? env->GetArrayLength(wordArray2) : 0; - jboolean result = dictionary->isValidBigram(word1, length1, word2, length2); - env->ReleaseIntArrayElements(wordArray2, word2, JNI_ABORT); - env->ReleaseIntArrayElements(wordArray1, word1, JNI_ABORT); - return result; + const jsize codePointLength1 = env->GetArrayLength(wordArray1); + const jsize codePointLength2 = env->GetArrayLength(wordArray2); + int codePoints1[codePointLength1]; + int codePoints2[codePointLength2]; + env->GetIntArrayRegion(wordArray1, 0, codePointLength1, codePoints1); + env->GetIntArrayRegion(wordArray2, 0, codePointLength2, codePoints2); + return dictionary->isValidBigram(codePoints1, codePointLength1, codePoints2, codePointLength2); } static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject object, - jcharArray before, jint beforeLength, jcharArray after, jint afterLength, jint score) { - jchar *beforeChars = env->GetCharArrayElements(before, 0); - jchar *afterChars = env->GetCharArrayElements(after, 0); - jfloat result = Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars, + jcharArray before, jcharArray after, jint score) { + jsize beforeLength = env->GetArrayLength(before); + jsize afterLength = env->GetArrayLength(after); + jchar beforeChars[beforeLength]; + jchar afterChars[afterLength]; + env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); + env->GetCharArrayRegion(after, 0, afterLength, afterChars); + return Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars, beforeLength, (unsigned short*)afterChars, afterLength, score); - env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT); - env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT); - return result; } static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, - jcharArray before, jint beforeLength, jcharArray after, jint afterLength) { - jchar *beforeChars = env->GetCharArrayElements(before, 0); - jchar *afterChars = env->GetCharArrayElements(after, 0); - jint result = Correction::RankingAlgorithm::editDistance( - (unsigned short*)beforeChars, beforeLength, (unsigned short*)afterChars, afterLength); - env->ReleaseCharArrayElements(after, afterChars, JNI_ABORT); - env->ReleaseCharArrayElements(before, beforeChars, JNI_ABORT); - return result; + jcharArray before, jcharArray after) { + jsize beforeLength = env->GetArrayLength(before); + jsize afterLength = env->GetArrayLength(after); + jchar beforeChars[beforeLength]; + jchar afterChars[afterLength]; + env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); + env->GetCharArrayRegion(after, 0, afterLength, afterChars); + return Correction::RankingAlgorithm::editDistance((unsigned short*)beforeChars, beforeLength, + (unsigned short*)afterChars, afterLength); } static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) { - Dictionary *dictionary = (Dictionary*)dict; + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return; void *dictBuf = dictionary->getDict(); if (!dictBuf) return; @@ -251,11 +252,11 @@ static JNINativeMethod sMethods[] = { {"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close}, {"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I", (void*) latinime_BinaryDictionary_getSuggestions}, - {"getFrequencyNative", "(J[II)I", (void*)latinime_BinaryDictionary_getFrequency}, + {"getFrequencyNative", "(J[I)I", (void*)latinime_BinaryDictionary_getFrequency}, {"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram}, - {"calcNormalizedScoreNative", "([CI[CII)F", + {"calcNormalizedScoreNative", "([C[CI)F", (void*)latinime_BinaryDictionary_calcNormalizedScore}, - {"editDistanceNative", "([CI[CI)I", (void*)latinime_BinaryDictionary_editDistance} + {"editDistanceNative", "([C[C)I", (void*)latinime_BinaryDictionary_editDistance} }; int register_BinaryDictionary(JNIEnv *env) { From 1b6829b0e0e7e73d4a6dc007114a63f2700c4d49 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 10 Aug 2012 11:02:41 +0900 Subject: [PATCH 020/133] Remove conservative MainKeyboardView.draw Change-Id: I52f58d1e013dfe1f1b0f8320cba903d3dbb4eabd --- .../inputmethod/keyboard/MainKeyboardView.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 4088f3e93..9590290ea 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -835,20 +835,6 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key return false; } - @Override - public void draw(Canvas c) { - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - super.draw(c); - tryGC = false; - } catch (OutOfMemoryError e) { - tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e); - } - } - } - /** * Receives hover events from the input framework. * From 31b90a368fa0c9bf22e65384d8a8f5e23d92bb29 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 11:26:40 +0900 Subject: [PATCH 021/133] Support a syntax to add whitelist entries in shortcuts Bug: 6906525 Change-Id: Ie33586c07973bf76e38390766175ba424d72d655 --- .../latin/dicttool/DictionaryMaker.java | 4 +- .../latin/dicttool/XmlDictInputOutput.java | 45 ++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java index 9ebd3bbdd..25e1740cb 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -112,7 +112,7 @@ public class DictionaryMaker { public static String getHelp() { return "Usage: makedict " - + "[-s [-b ] [-c ] " + + "[-s [-b ] [-c ] " + "| -s ] [-d ] " + "[-d1 ] [-x ] [-2]\n" + "\n" @@ -246,7 +246,7 @@ public class DictionaryMaker { * Read a dictionary from a unigram XML file, and optionally a bigram XML file. * * @param unigramXmlFilename the name of the unigram XML file. May not be null. - * @param shortcutXmlFilename the name of the shortcut XML file, or null if there is none. + * @param shortcutXmlFilename the name of the shortcut/whitelist XML file, or null if none. * @param bigramXmlFilename the name of the bigram XML file. Pass null if there are no bigrams. * @return the read dictionary. * @throws FileNotFoundException if one of the files can't be found diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java index 8e2e73505..9ab56d797 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java @@ -179,7 +179,7 @@ public class XmlDictInputOutput { mSrc = attrs.getValue(uri, SRC_ATTRIBUTE); } else if (DST_TAG.equals(localName)) { String dst = attrs.getValue(uri, DST_ATTRIBUTE); - int freq = Integer.parseInt(attrs.getValue(uri, DST_FREQ)); + int freq = getValueFromFreqString(attrs.getValue(uri, DST_FREQ)); WeightedString bigram = new WeightedString(dst, freq / XML_TO_MEMORY_RATIO); ArrayList bigramList = mAssocMap.get(mSrc); if (null == bigramList) bigramList = new ArrayList(); @@ -188,6 +188,10 @@ public class XmlDictInputOutput { } } + protected int getValueFromFreqString(final String freqString) { + return Integer.parseInt(freqString); + } + // This may return an empty map, but will never return null. public HashMap> getAssocMap() { return mAssocMap; @@ -216,22 +220,40 @@ public class XmlDictInputOutput { } /** - * SAX handler for a shortcut XML file. + * SAX handler for a shortcut & whitelist XML file. */ - static private class ShortcutHandler extends AssociativeListHandler { + static private class ShortcutAndWhitelistHandler extends AssociativeListHandler { private final static String ENTRY_TAG = "entry"; private final static String ENTRY_ATTRIBUTE = "shortcut"; private final static String TARGET_TAG = "target"; private final static String REPLACEMENT_ATTRIBUTE = "replacement"; private final static String TARGET_PRIORITY_ATTRIBUTE = "priority"; + private final static String WHITELIST_MARKER = "whitelist"; + private final static int WHITELIST_FREQ_VALUE = 15; + private final static int MIN_FREQ = 0; + private final static int MAX_FREQ = 14; - public ShortcutHandler() { + public ShortcutAndWhitelistHandler() { super(ENTRY_TAG, ENTRY_ATTRIBUTE, TARGET_TAG, REPLACEMENT_ATTRIBUTE, TARGET_PRIORITY_ATTRIBUTE); } + @Override + protected int getValueFromFreqString(final String freqString) { + if (WHITELIST_MARKER.equals(freqString)) { + return WHITELIST_FREQ_VALUE; + } else { + final int intValue = super.getValueFromFreqString(freqString); + if (intValue < MIN_FREQ || intValue > MAX_FREQ) { + throw new RuntimeException("Shortcut freq out of range. Accepted range is " + + MIN_FREQ + ".." + MAX_FREQ); + } + return intValue; + } + } + // As per getAssocMap(), this never returns null. - public HashMap> getShortcutMap() { + public HashMap> getShortcutAndWhitelistMap() { return getAssocMap(); } } @@ -243,7 +265,7 @@ public class XmlDictInputOutput { * representation. * * @param unigrams the file to read the data from. - * @param shortcuts the file to read the shortcuts from, or null. + * @param shortcuts the file to read the shortcuts & whitelist from, or null. * @param bigrams the file to read the bigrams from, or null. * @return the in-memory representation of the dictionary. */ @@ -256,11 +278,12 @@ public class XmlDictInputOutput { final BigramHandler bigramHandler = new BigramHandler(); if (null != bigrams) parser.parse(bigrams, bigramHandler); - final ShortcutHandler shortcutHandler = new ShortcutHandler(); - if (null != shortcuts) parser.parse(shortcuts, shortcutHandler); + final ShortcutAndWhitelistHandler shortcutAndWhitelistHandler = + new ShortcutAndWhitelistHandler(); + if (null != shortcuts) parser.parse(shortcuts, shortcutAndWhitelistHandler); final UnigramHandler unigramHandler = - new UnigramHandler(shortcutHandler.getShortcutMap()); + new UnigramHandler(shortcutAndWhitelistHandler.getShortcutAndWhitelistMap()); parser.parse(unigrams, unigramHandler); final FusionDictionary dict = unigramHandler.getFinalDictionary(); final HashMap> bigramMap = bigramHandler.getBigramMap(); @@ -280,7 +303,7 @@ public class XmlDictInputOutput { * * This method reads data from the parser and creates a new FusionDictionary with it. * The format parsed by this method is the format used before Ice Cream Sandwich, - * which has no support for bigrams or shortcuts. + * which has no support for bigrams or shortcuts/whitelist. * It is important to note that this method expects the parser to have already eaten * the first, all-encompassing tag. * @@ -291,7 +314,7 @@ public class XmlDictInputOutput { /** * Writes a dictionary to an XML file. * - * The output format is the "second" format, which supports bigrams and shortcuts. + * The output format is the "second" format, which supports bigrams and shortcuts/whitelist. * * @param destination a destination stream to write to. * @param dict the dictionary to write. From e9f3e182e4b3217282831fd8805958270b4dbba3 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Thu, 9 Aug 2012 23:23:08 +0900 Subject: [PATCH 022/133] Add arguments for dic traverse session Change-Id: I8776ad528eeea9a5fc4dcf394ca13bb80c04fb6d --- .../inputmethod/latin/BinaryDictionary.java | 1 + .../inputmethod/latin/DicTraverseSession.java | 14 ++-- native/jni/Android.mk | 1 + ...d_inputmethod_latin_DicTraverseSession.cpp | 20 +++--- ...oid_inputmethod_latin_DicTraverseSession.h | 27 -------- native/jni/src/dic_traverse_wrapper.cpp | 26 ++++++++ native/jni/src/dic_traverse_wrapper.h | 66 +++++++++++++++++++ native/jni/src/dictionary.cpp | 16 +++-- native/jni/src/dictionary.h | 4 +- .../jni/src/gesture/gesture_decoder_wrapper.h | 7 -- .../gesture/incremental_decoder_interface.h | 3 +- .../src/gesture/incremental_decoder_wrapper.h | 7 -- 12 files changed, 124 insertions(+), 68 deletions(-) create mode 100644 native/jni/src/dic_traverse_wrapper.cpp create mode 100644 native/jni/src/dic_traverse_wrapper.h diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 6ac6e83a3..7ba565c31 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -98,6 +98,7 @@ public class BinaryDictionary extends Dictionary { private static native float calcNormalizedScoreNative(char[] before, char[] after, int score); private static native int editDistanceNative(char[] before, char[] after); + // TODO: Move native dict into session private final void loadDictionary(String path, long startOffset, long length) { mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS); diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 437876e05..160752896 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -23,8 +23,8 @@ public class DicTraverseSession { JniUtils.loadNativeLibrary(); } private native long setDicTraverseSessionNative(String locale); - private native void initDicTraverseSessionNative( - long nativeDicTraverseSession, int[] previousWord, int previwousWordLength); + private native void initDicTraverseSessionNative(long nativeDicTraverseSession, + long dictionary, int[] previousWord, int previwousWordLength); private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); private long mNativeDicTraverseSession; @@ -32,19 +32,19 @@ public class DicTraverseSession { public DicTraverseSession(Locale locale) { mNativeDicTraverseSession = createNativeDicTraverseSession( locale != null ? locale.toString() : ""); - initSession(); } public long getSession() { return mNativeDicTraverseSession; } - public void initSession() { - initSession(null, 0); + public void initSession(long dictionary) { + initSession(dictionary, null, 0); } - public void initSession(int[] previousWord, int previousWordLength) { - initDicTraverseSessionNative(mNativeDicTraverseSession, previousWord, previousWordLength); + public void initSession(long dictionary, int[] previousWord, int previousWordLength) { + initDicTraverseSessionNative( + mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); } private final long createNativeDicTraverseSession(String locale) { diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 1b7301b19..86ad8576e 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -47,6 +47,7 @@ LATIN_IME_CORE_SRC_FILES := \ char_utils.cpp \ correction.cpp \ dictionary.cpp \ + dic_traverse_wrapper.cpp \ proximity_info.cpp \ proximity_info_state.cpp \ unigram_dictionary.cpp \ diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 0f5c39642..f0fa2db2a 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -17,25 +17,23 @@ #define LOG_TAG "LatinIME: jni: Session" #include "com_android_inputmethod_latin_DicTraverseSession.h" +#include "dic_traverse_wrapper.h" #include "jni.h" #include "jni_common.h" namespace latinime { -void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)() = 0; -void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; -void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)( - JNIEnv *, void *, const jintArray, const jint) = 0; - -static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, - jstring localejStr) { - void *traverseSession = DicTraverseWrapper::getDicTraverseSession(); +static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring localeJStr) { + void *traverseSession = DicTraverseWrapper::getDicTraverseSession(env, localeJStr); return reinterpret_cast(traverseSession); } static void latinime_initDicTraverseSession(JNIEnv *env, jlong traverseSession, - jintArray previousWord, jint previousWordLength) { + jlong dictionary, jintArray previousWord, jint previousWordLength) { void *ts = reinterpret_cast(traverseSession); - DicTraverseWrapper::initDicTraverseSession(env, ts, previousWord, previousWordLength); + Dictionary *dict = reinterpret_cast(dictionary); + int prevWord[previousWordLength]; + env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); + DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength); } static void latinime_DicTraverseSession_release( @@ -46,7 +44,7 @@ static void latinime_DicTraverseSession_release( static JNINativeMethod sMethods[] = { {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, - {"initDicTraverseSessionNative", "(J[II)V", (void*)latinime_initDicTraverseSession}, + {"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_DicTraverseSession_release} }; diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h index a76815d7f..37531e96b 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h @@ -21,33 +21,6 @@ #include "jni.h" namespace latinime { - -// TODO: Remove -class DicTraverseWrapper { - public: - static void *getDicTraverseSession() { - if (sDicTraverseSessionFactoryMethod) { - return sDicTraverseSessionFactoryMethod(); - } - return 0; - } - static void initDicTraverseSession(JNIEnv *env, void *traverseSession, - const jintArray prevWord, const jint prevWordLength) { - if (sDicTraverseSessionInitMethod) { - sDicTraverseSessionInitMethod(env, traverseSession, prevWord, prevWordLength); - } - } - static void releaseDicTraverseSession(void *traverseSession) { - if (sDicTraverseSessionReleaseMethod) { - sDicTraverseSessionReleaseMethod(traverseSession); - } - } - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); - static void *(*sDicTraverseSessionFactoryMethod)(); - static void (*sDicTraverseSessionInitMethod)(JNIEnv *, void *, const jintArray, const jint); - static void (*sDicTraverseSessionReleaseMethod)(void *); -}; int register_DicTraverseSession(JNIEnv *env); } // namespace latinime #endif // _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H diff --git a/native/jni/src/dic_traverse_wrapper.cpp b/native/jni/src/dic_traverse_wrapper.cpp new file mode 100644 index 000000000..1f7dcbfb2 --- /dev/null +++ b/native/jni/src/dic_traverse_wrapper.cpp @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#define LOG_TAG "LatinIME: jni: Session" + +#include "dic_traverse_wrapper.h" + +namespace latinime { +void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *env, jstring locale) = 0; +void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; +void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)( + void *, Dictionary *, const int *, const int) = 0; +} // namespace latinime diff --git a/native/jni/src/dic_traverse_wrapper.h b/native/jni/src/dic_traverse_wrapper.h new file mode 100644 index 000000000..8396d0027 --- /dev/null +++ b/native/jni/src/dic_traverse_wrapper.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef LATINIME_DIC_TRAVERSE_WRAPPER_H +#define LATINIME_DIC_TRAVERSE_WRAPPER_H + +#include + +#include "defines.h" +#include "jni.h" + +namespace latinime { +class Dictionary; +// TODO: Remove +class DicTraverseWrapper { + public: + static void *getDicTraverseSession(JNIEnv *env, jstring locale) { + if (sDicTraverseSessionFactoryMethod) { + return sDicTraverseSessionFactoryMethod(env, locale); + } + return 0; + } + static void initDicTraverseSession(void *traverseSession, + Dictionary *dictionary, const int *prevWord, const int prevWordLength) { + if (sDicTraverseSessionInitMethod) { + sDicTraverseSessionInitMethod(traverseSession, dictionary, prevWord, prevWordLength); + } + } + static void releaseDicTraverseSession(void *traverseSession) { + if (sDicTraverseSessionReleaseMethod) { + sDicTraverseSessionReleaseMethod(traverseSession); + } + } + static void setTraverseSessionFactoryMethod( + void *(*factoryMethod)(JNIEnv *env, jstring locale)) { + sDicTraverseSessionFactoryMethod = factoryMethod; + } + static void setTraverseSessionInitMethod( + void (*initMethod)(void *, Dictionary *, const int *, const int)) { + sDicTraverseSessionInitMethod = initMethod; + } + static void setTraverseSessionReleaseMethod(void (*releaseMethod)(void *)) { + sDicTraverseSessionReleaseMethod = releaseMethod; + } + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); + static void *(*sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring); + static void (*sDicTraverseSessionInitMethod)(void *, Dictionary *, const int *, const int); + static void (*sDicTraverseSessionReleaseMethod)(void *); +}; +int register_DicTraverseSession(JNIEnv *env); +} // namespace latinime +#endif // LATINIME_DIC_TRAVERSE_WRAPPER_H diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index 8c785a263..f3bdb310d 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -22,6 +22,7 @@ #include "binary_format.h" #include "defines.h" #include "dictionary.h" +#include "dic_traverse_wrapper.h" #include "gesture_decoder_wrapper.h" #include "unigram_dictionary.h" @@ -31,8 +32,9 @@ namespace latinime { Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions) - : mDict((unsigned char*) dict), mDictSize(dictSize), - mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) { + : mDict((unsigned char*) dict), + mOffsetDict(((unsigned char*) dict) + BinaryFormat::getHeaderSize(mDict)), + mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) { if (DEBUG_DICT) { if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) { AKLOGI("Max word length (%d) is greater than %d", @@ -40,14 +42,13 @@ Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, AKLOGI("IN NATIVE SUGGEST Version: %d", (mDict[0] & 0xFF)); } } - const unsigned int headerSize = BinaryFormat::getHeaderSize(mDict); const unsigned int options = BinaryFormat::getFlags(mDict); - mUnigramDictionary = new UnigramDictionary(mDict + headerSize, typedLetterMultiplier, + mUnigramDictionary = new UnigramDictionary(mOffsetDict, typedLetterMultiplier, fullWordMultiplier, maxWordLength, maxWords, options); - mBigramDictionary = new BigramDictionary(mDict + headerSize, maxWordLength, maxPredictions); + mBigramDictionary = new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions); mGestureDecoder = new GestureDecoderWrapper(maxWordLength, maxWords); mGestureDecoder->setDict(mUnigramDictionary, mBigramDictionary, - mDict + headerSize /* dict root */, 0 /* root pos */); + mOffsetDict /* dict root */, 0 /* root pos */); } Dictionary::~Dictionary() { @@ -64,7 +65,8 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSessi int *frequencies, int *spaceIndices, int *outputTypes) { int result = 0; if (isGesture) { - mGestureDecoder->setPrevWord(prevWordChars, prevWordLength); + DicTraverseWrapper::initDicTraverseSession( + traverseSession, this, prevWordChars, prevWordLength); result = mGestureDecoder->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, codes, codesSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes); diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h index 2c79527be..b550ba4df 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/dictionary.h @@ -55,7 +55,8 @@ class Dictionary { int getFrequency(const int32_t *word, int length) const; bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const; - void *getDict() const { return (void *)mDict; } + void *getDict() const { return (void *)mDict; } // required to release dictionary buffer + void *getOffsetDict() const { return (void *)mOffsetDict; } int getDictSize() const { return mDictSize; } int getMmapFd() const { return mMmapFd; } int getDictBufAdjust() const { return mDictBufAdjust; } @@ -68,6 +69,7 @@ class Dictionary { private: DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary); const unsigned char *mDict; + const unsigned char *mOffsetDict; // Used only for the mmap version of dictionary loading, but we use these as dummy variables // also for the malloc version. diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index b70c8e0a3..14a0705f7 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -63,13 +63,6 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { mIncrementalDecoderInterface->setDict(dict, bigram, dictRoot, rootPos); } - void setPrevWord(const int32_t *prevWord, int prevWordLength) { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->setPrevWord(prevWord, prevWordLength); - } - static void setGestureDecoderFactoryMethod( IncrementalDecoderInterface *(*factoryMethod)(int, int)) { sGestureDecoderFactoryMethod = factoryMethod; diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index e8d3a5333..cc219bf50 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -35,8 +35,9 @@ class IncrementalDecoderInterface { virtual void reset() = 0; virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) = 0; - virtual void setPrevWord(const int32_t *prevWord, int prevWordLength) = 0; virtual ~IncrementalDecoderInterface() { }; + private: + //DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderInterface); }; } // namespace latinime #endif // LATINIME_INCREMENTAL_DECODER_INTERFACE_H diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 8d66b4ec1..2f9602853 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -63,13 +63,6 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { mIncrementalDecoderInterface->setDict(dict, bigram, dictRoot, rootPos); } - void setPrevWord(const int32_t *prevWord, int prevWordLength) { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->setPrevWord(prevWord, prevWordLength); - } - static void setIncrementalDecoderFactoryMethod( IncrementalDecoderInterface *(*factoryMethod)(int, int)) { sIncrementalDecoderFactoryMethod = factoryMethod; From 1d8103ea57cb20a37f86a4e1fe8a514cd1eda8a2 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 12:00:40 +0900 Subject: [PATCH 023/133] Add a shortcut-format version of the whitelist. This will ultimately replace the whitelist resource, but this change doesn't delete it to avoid removing the functionality temporarily. Bug: 6906525 Change-Id: I576edc42cd2a964b86b7597f1ede1cf6ec8e26c3 --- dictionaries/en_whitelist.xml | 297 ++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 dictionaries/en_whitelist.xml diff --git a/dictionaries/en_whitelist.xml b/dictionaries/en_whitelist.xml new file mode 100644 index 000000000..f112077bd --- /dev/null +++ b/dictionaries/en_whitelist.xml @@ -0,0 +1,297 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 565fdeee7d989391c4624d2d612da2f0aba6a12e Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 12:02:16 +0900 Subject: [PATCH 024/133] Reinstate putting shortcut-only entries in the dictionary Not sure at which point this functionality got lost, but anyway, it's back Bug: 6906525 Change-Id: Ie87baa0dacdcee90e029917ca866099593d5d048 --- .../inputmethod/latin/dicttool/XmlDictInputOutput.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java index 9ab56d797..9ce8c4934 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java @@ -90,6 +90,10 @@ public class XmlDictInputOutput { public FusionDictionary getFinalDictionary() { final FusionDictionary dict = mDictionary; + for (final String shortcutOnly : mShortcutsMap.keySet()) { + if (dict.hasWord(shortcutOnly)) continue; + dict.add(shortcutOnly, 0, mShortcutsMap.get(shortcutOnly)); + } mDictionary = null; mShortcutsMap.clear(); mWord = ""; From 926ef06a1ef8a0f8e01baa26c23172b93012d2b1 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 13:14:45 +0900 Subject: [PATCH 025/133] Pass the type from native code all the way to Java. Bug: 6906525 Change-Id: I057390d47a223450e22d8338509e22c28fc0d5f6 --- .../inputmethod/latin/BinaryDictionary.java | 5 +++-- native/jni/src/unigram_dictionary.cpp | 18 ++++++++++-------- native/jni/src/words_priority_queue.h | 17 ++++++++++------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 6ac6e83a3..b6329187d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -143,9 +143,10 @@ public class BinaryDictionary extends Dictionary { ++len; } if (len > 0) { + final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j] + ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j]; suggestions.add(new SuggestedWordInfo( - new String(mOutputChars, start, len), - mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, mDictType)); + new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType)); } } return suggestions; diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index b6b0210cc..9f7ab5362 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -63,8 +63,8 @@ static inline unsigned int getCodesBufferSize(const int *codes, const int codesS // TODO: This needs to take a const unsigned short* and not tinker with its contents static inline void addWord( - unsigned short *word, int length, int frequency, WordsPriorityQueue *queue) { - queue->push(frequency, word, length); + unsigned short *word, int length, int frequency, WordsPriorityQueue *queue, int type) { + queue->push(frequency, word, length, type); } // Return the replacement code point for a digraph, or 0 if none. @@ -213,8 +213,8 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, AKLOGI("Max normalized score = %f", ns); } const int suggestedWordsCount = - queuePool.getMasterQueue()->outputSuggestions( - masterCorrection.getPrimaryInputWord(), codesSize, frequencies, outWords); + queuePool.getMasterQueue()->outputSuggestions(masterCorrection.getPrimaryInputWord(), + codesSize, frequencies, outWords, outputTypes); if (DEBUG_DICT) { float ns = queuePool.getMasterQueue()->getHighestNormalizedScore( @@ -391,7 +391,8 @@ inline void UnigramDictionary::onTerminal(const int probability, const int finalProbability = correction->getFinalProbability(probability, &wordPointer, &wordLength); if (finalProbability != NOT_A_PROBABILITY) { - addWord(wordPointer, wordLength, finalProbability, masterQueue); + addWord(wordPointer, wordLength, finalProbability, masterQueue, + Dictionary::KIND_CORRECTION); const int shortcutProbability = finalProbability > 0 ? finalProbability - 1 : 0; // Please note that the shortcut candidates will be added to the master queue only. @@ -409,7 +410,7 @@ inline void UnigramDictionary::onTerminal(const int probability, const int shortcutTargetStringLength = iterator.getNextShortcutTarget( MAX_WORD_LENGTH_INTERNAL, shortcutTarget); addWord(shortcutTarget, shortcutTargetStringLength, shortcutProbability, - masterQueue); + masterQueue, Dictionary::KIND_CORRECTION); } } } @@ -424,7 +425,7 @@ inline void UnigramDictionary::onTerminal(const int probability, } const int finalProbability = correction->getFinalProbabilityForSubQueue( probability, &wordPointer, &wordLength, inputIndex); - addWord(wordPointer, wordLength, finalProbability, subQueue); + addWord(wordPointer, wordLength, finalProbability, subQueue, Dictionary::KIND_CORRECTION); } } @@ -572,7 +573,8 @@ int UnigramDictionary::getSubStringSuggestion( AKLOGI("Split two words: freq = %d, length = %d, %d, isSpace ? %d", pairFreq, inputLength, tempOutputWordLength, isSpaceProximity); } - addWord(outputWord, tempOutputWordLength, pairFreq, queuePool->getMasterQueue()); + addWord(outputWord, tempOutputWordLength, pairFreq, queuePool->getMasterQueue(), + Dictionary::KIND_CORRECTION); } return FLAG_MULTIPLE_SUGGEST_CONTINUE; } diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h index c0dedb59d..8a6da1c95 100644 --- a/native/jni/src/words_priority_queue.h +++ b/native/jni/src/words_priority_queue.h @@ -33,12 +33,14 @@ class WordsPriorityQueue { unsigned short mWord[MAX_WORD_LENGTH_INTERNAL]; int mWordLength; bool mUsed; + int mType; - void setParams(int score, unsigned short *word, int wordLength) { + void setParams(int score, unsigned short *word, int wordLength, int type) { mScore = score; mWordLength = wordLength; memcpy(mWord, word, sizeof(unsigned short) * wordLength); mUsed = true; + mType = type; } }; @@ -56,7 +58,7 @@ class WordsPriorityQueue { delete[] mSuggestedWords; } - void push(int score, unsigned short *word, int wordLength) { + void push(int score, unsigned short *word, int wordLength, int type) { SuggestedWord *sw = 0; if (mSuggestions.size() >= MAX_WORDS) { sw = mSuggestions.top(); @@ -69,9 +71,9 @@ class WordsPriorityQueue { } } if (sw == 0) { - sw = getFreeSuggestedWord(score, word, wordLength); + sw = getFreeSuggestedWord(score, word, wordLength, type); } else { - sw->setParams(score, word, wordLength); + sw->setParams(score, word, wordLength, type); } if (sw == 0) { AKLOGE("SuggestedWord is accidentally null."); @@ -94,7 +96,7 @@ class WordsPriorityQueue { } int outputSuggestions(const unsigned short *before, const int beforeLength, - int *frequencies, unsigned short *outputChars) { + int *frequencies, unsigned short *outputChars, int* outputTypes) { mHighestSuggestedWord = 0; const unsigned int size = min( MAX_WORDS, static_cast(mSuggestions.size())); @@ -140,6 +142,7 @@ class WordsPriorityQueue { const unsigned int wordLength = sw->mWordLength; char *targetAdr = (char*) outputChars + i * MAX_WORD_LENGTH * sizeof(short); frequencies[i] = sw->mScore; + outputTypes[i] = sw->mType; memcpy(targetAdr, sw->mWord, (wordLength) * sizeof(short)); if (wordLength < MAX_WORD_LENGTH) { ((unsigned short*) targetAdr)[wordLength] = 0; @@ -191,10 +194,10 @@ class WordsPriorityQueue { }; SuggestedWord *getFreeSuggestedWord(int score, unsigned short *word, - int wordLength) { + int wordLength, int type) { for (unsigned int i = 0; i < MAX_WORD_LENGTH; ++i) { if (!mSuggestedWords[i].mUsed) { - mSuggestedWords[i].setParams(score, word, wordLength); + mSuggestedWords[i].setParams(score, word, wordLength, type); return &mSuggestedWords[i]; } } From 6b4a1d79eba19a55715e20b4ee75b3934f397db2 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 10 Aug 2012 15:42:56 +0900 Subject: [PATCH 026/133] Step 22-A implement session Change-Id: Ida63382ffb756e20cc5b17c2702d2895e7e6e889 --- native/jni/src/geometry_utils.h | 101 ++++++++++++++++++++++++++++++ native/jni/src/proximity_info.cpp | 64 +++++++++++++++++++ native/jni/src/proximity_info.h | 18 ++++++ 3 files changed, 183 insertions(+) create mode 100644 native/jni/src/geometry_utils.h diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h new file mode 100644 index 000000000..ada889e11 --- /dev/null +++ b/native/jni/src/geometry_utils.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef LATINIME_INCREMENTAL_GEOMETRY_UTILS_H +#define LATINIME_INCREMENTAL_GEOMETRY_UTILS_H + +#include + +#define MAX_DISTANCE 10000000 +#define KEY_NUM 27 +#define SPACE_KEY 26 +#define MAX_PATHS 2 + +#define DEBUG_DECODER false + +namespace latinime { + +static inline float sqr(float x) { + return x * x; +} + +static inline float getNormalizedSqrDistance(int x1, int y1, int x2, int y2, int scale) { + return sqr((x1 - x2) * 1.0 / scale) + sqr((y1 - y2) * 1.0 / scale); +} + +static inline int getDistance(int x1, int y1, int x2, int y2) { + return (int) sqrt(sqr(x2 - x1) + sqr(y2 - y1)); +} + +static inline float getDistanceSq(float x1, float y1, float x2, float y2) { + return sqr(x2 - x1) + sqr(y2 - y1); +} + +static inline float getAngle(int x1, int y1, int x2, int y2) { + float dx = x1 - x2; + float dy = y1 - y2; + if (dx == 0 && dy == 0) + return 0; + return atan2(dy, dx); +} + +static inline float angleDiff(float a1, float a2) { + float diff = a1 - a2; + if (diff < 0) { + diff = -diff; + } + if (diff > M_PI) { + return 2 * M_PI - diff; + } + return diff; +} + +//static float pointToLineDistanceSq(float x, float y, float x1, float y1, float x2, float y2) { +// float A = x - x1; +// float B = y - y1; +// float C = x2 - x1; +// float D = y2 - y1; +// return abs(A * D - C * B) / sqrt(C * C + D * D); +//} + +static inline float pointToLineSegDistanceSq( + float x, float y, float x1, float y1, float x2, float y2) { + float ray1x = x - x1; + float ray1y = y - y1; + float ray2x = x2 - x1; + float ray2y = y2 - y1; + + float dotProduct = ray1x * ray2x + ray1y * ray2y; + float lineLengthSq = ray2x * ray2x + ray2y * ray2y; + float projectionLengthSq = dotProduct / lineLengthSq; + + float projectionX, projectionY; + if (projectionLengthSq < 0) { + projectionX = x1; + projectionY = y1; + } else if (projectionLengthSq > 1) { + projectionX = x2; + projectionY = y2; + } else { + projectionX = x1 + projectionLengthSq * ray2x; + projectionY = y1 + projectionLengthSq * ray2y; + } + + float dist = getDistanceSq(x, y, projectionX, projectionY); + return dist; +} +} // namespace latinime +#endif // LATINIME_INCREMENTAL_GEOMETRY_UTILS_H diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 2564c8a67..2633913f7 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -23,6 +23,7 @@ #include "additional_proximity_chars.h" #include "char_utils.h" #include "defines.h" +#include "geometry_utils.h" #include "jni.h" #include "proximity_info.h" @@ -55,6 +56,7 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma const jfloatArray sweetSpotRadii) : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), + MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), @@ -84,6 +86,7 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma safeGetOrFillZeroFloatArrayRegion(env, sweetSpotCenterYs, KEY_COUNT, mSweetSpotCenterYs); safeGetOrFillZeroFloatArrayRegion(env, sweetSpotRadii, KEY_COUNT, mSweetSpotRadii); initializeCodeToKeyIndex(); + initializeG(); } // Build the reversed look up table from the char code to the index in mKeyXCoordinates, @@ -222,6 +225,67 @@ int ProximityInfo::getKeyIndex(const int c) const { return mCodeToKeyIndex[baseLowerC]; } +int ProximityInfo::getKeyCode(const int keyIndex) const { + if (keyIndex < 0 || keyIndex >= KEY_COUNT) { + return NOT_AN_INDEX; + } + return mKeyToCodeIndexG[keyIndex]; +} + +void ProximityInfo::initializeG() { + for (int i = 0; i < KEY_COUNT; ++i) { + const int code = mKeyCharCodes[i]; + const int lowerCode = toBaseLowerCase(code); + mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; + mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; + if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) { + mCodeToKeyIndex[lowerCode] = i; + mKeyToCodeIndexG[i] = lowerCode; + } else { + mKeyToCodeIndexG[i] = code; + } + } + for (int i = 0; i < KEY_COUNT; i++) { + mKeyKeyDistancesG[i][i] = 0; + for (int j = i + 1; j < KEY_COUNT; j++) { + mKeyKeyDistancesG[i][j] = getDistance( + mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); + mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; + } + } +} + +float ProximityInfo::getKeyCenterXOfCharG(int charCode) const { + return getKeyCenterXOfIdG(getKeyIndex(charCode)); +} + +float ProximityInfo::getKeyCenterYOfCharG(int charCode) const { + return getKeyCenterYOfIdG(getKeyIndex(charCode)); +} + +float ProximityInfo::getKeyCenterXOfIdG(int keyId) const { + if (keyId >= 0) { + return mCenterXsG[keyId]; + } + return 0; +} + +float ProximityInfo::getKeyCenterYOfIdG(int keyId) const { + if (keyId >= 0) { + return mCenterYsG[keyId]; + } + return 0; +} + +int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const { + const int keyId0 = getKeyIndex(key0); + const int keyId1 = getKeyIndex(key1); + if (keyId0 >= 0 && keyId1 >= 0) { + return mKeyKeyDistancesG[keyId0][keyId1]; + } + return 0; +} + // TODO: [Staging] Optimize void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex, int *keyToCodeIndex, int *keyCount, int *keyWidth) const { diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index d7e24c5f5..58f2d7502 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -49,6 +49,7 @@ class ProximityInfo { return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom; } int getKeyIndex(const int c) const; + int getKeyCode(const int keyIndex) const; bool hasSweetSpotData(const int keyIndex) const { // When there are no calibration data for a key, // the radius of the key is assigned to zero. @@ -70,6 +71,10 @@ class ProximityInfo { return HAS_TOUCH_POSITION_CORRECTION_DATA; } + int getMostCommonKeyWidth() const { + return MOST_COMMON_KEY_WIDTH; + } + int getMostCommonKeyWidthSquare() const { return MOST_COMMON_KEY_WIDTH_SQUARE; } @@ -98,6 +103,12 @@ class ProximityInfo { return GRID_HEIGHT; } + float getKeyCenterXOfCharG(int charCode) const; + float getKeyCenterYOfCharG(int charCode) const; + float getKeyCenterXOfIdG(int keyId) const; + float getKeyCenterYOfIdG(int keyId) const; + int getKeyKeyDistanceG(int key0, int key1) const; + // Returns the keyboard key-center information. void getCenters(int *centersX, int *centersY, int *codeToKeyIndex, int *keyToCodeIndex, int *keyCount, int *keyWidth) const; @@ -113,6 +124,7 @@ class ProximityInfo { int getStartIndexFromCoordinates(const int x, const int y) const; void initializeCodeToKeyIndex(); + void initializeG(); float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const; float calculateSquaredDistanceFromSweetSpotCenter( const int keyIndex, const int inputIndex) const; @@ -123,6 +135,7 @@ class ProximityInfo { const int KEYBOARD_HEIGHT; const int GRID_WIDTH; const int GRID_HEIGHT; + const int MOST_COMMON_KEY_WIDTH; const int MOST_COMMON_KEY_WIDTH_SQUARE; const int CELL_WIDTH; const int CELL_HEIGHT; @@ -139,6 +152,11 @@ class ProximityInfo { float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD]; float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD]; int mCodeToKeyIndex[MAX_CHAR_CODE + 1]; + + int mKeyToCodeIndexG[MAX_KEY_COUNT_IN_A_KEYBOARD]; + int mCenterXsG[MAX_KEY_COUNT_IN_A_KEYBOARD]; + int mCenterYsG[MAX_KEY_COUNT_IN_A_KEYBOARD]; + int mKeyKeyDistancesG[MAX_KEY_COUNT_IN_A_KEYBOARD][MAX_KEY_COUNT_IN_A_KEYBOARD]; // TODO: move to correction.h }; } // namespace latinime From caed149b67be378adf49f3db16a2cfbb8dd15d84 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 14:31:12 +0900 Subject: [PATCH 027/133] Optimization & Simplification It's useless to do the whitelist lookup twice. Also, putting this test out of this method will allow whitelist entries to come from other sources. Bug: 6906525 Change-Id: I4afe678cae6556d16642d155ce770fbf4e61ad49 --- .../inputmethod/latin/AutoCorrection.java | 11 ++--------- .../com/android/inputmethod/latin/Suggest.java | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index a66337404..048166807 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -75,17 +75,10 @@ public class AutoCorrection { return maxFreq; } - // Returns true if this is a whitelist entry, or it isn't in any dictionary. - public static boolean isWhitelistedOrNotAWord( + // Returns true if this isn't in any dictionary. + public static boolean isNotAWord( final ConcurrentHashMap dictionaries, final CharSequence word, final boolean ignoreCase) { - final WhitelistDictionary whitelistDictionary = - (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST); - // If "word" is in the whitelist dictionary, it should not be auto corrected. - if (whitelistDictionary != null - && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) { - return true; - } return !isValidWord(dictionaries, word, ignoreCase); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index a65d36adb..ce27ef4af 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -208,14 +208,6 @@ public class Suggest { wordComposerForLookup, prevWordForBigram, proximityInfo)); } - // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" - // but still autocorrected from - in the case the whitelist only capitalizes the word. - // The whitelist should be case-insensitive, so it's not possible to be consistent with - // a boolean flag. Right now this is handled with a slight hack in - // WhitelistDictionary#shouldForciblyAutoCorrectFrom. - final boolean allowsToBeAutoCorrected = AutoCorrection.isWhitelistedOrNotAWord( - mDictionaries, consideredWord, wordComposer.isFirstCharCapitalized()); - final CharSequence whitelistedWord = mWhiteListDictionary.getWhitelistedWord(consideredWord); if (whitelistedWord != null) { @@ -225,6 +217,16 @@ public class Suggest { Dictionary.TYPE_WHITELIST)); } + // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" + // but still autocorrected from - in the case the whitelist only capitalizes the word. + // The whitelist should be case-insensitive, so it's not possible to be consistent with + // a boolean flag. Right now this is handled with a slight hack in + // WhitelistDictionary#shouldForciblyAutoCorrectFrom. + final boolean allowsToBeAutoCorrected = (null != whitelistedWord + && !whitelistedWord.equals(consideredWord)) + || AutoCorrection.isNotAWord(mDictionaries, consideredWord, + wordComposer.isFirstCharCapitalized()); + final boolean hasAutoCorrection; // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because // any attempt to do auto-correction is already shielded with a test for this flag; at the From 7b258e512dc2a8c821eb9f435e5719b8a967b441 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 14:50:30 +0900 Subject: [PATCH 028/133] Accept whitelisted entries from any source. ...not only the WhitelistDictionary. Bug: 6906525 Change-Id: I7f181eb6936ec17bbccdc4e736fd09292af24e9c --- .../com/android/inputmethod/latin/Suggest.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index ce27ef4af..8a2341d5e 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -208,15 +208,24 @@ public class Suggest { wordComposerForLookup, prevWordForBigram, proximityInfo)); } - final CharSequence whitelistedWord = + final CharSequence whitelistedWordFromWhitelistDictionary = mWhiteListDictionary.getWhitelistedWord(consideredWord); - if (whitelistedWord != null) { + if (whitelistedWordFromWhitelistDictionary != null) { // MAX_SCORE ensures this will be considered strong enough to be auto-corrected - suggestionsSet.add(new SuggestedWordInfo(whitelistedWord, + suggestionsSet.add(new SuggestedWordInfo(whitelistedWordFromWhitelistDictionary, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST, Dictionary.TYPE_WHITELIST)); } + final CharSequence whitelistedWord; + if (suggestionsSet.isEmpty()) { + whitelistedWord = null; + } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { + whitelistedWord = null; + } else { + whitelistedWord = suggestionsSet.first().mWord; + } + // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" // but still autocorrected from - in the case the whitelist only capitalizes the word. // The whitelist should be case-insensitive, so it's not possible to be consistent with From 88dfeb30bf84ed9af6d46f0f68b58bcb337f7bb9 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 10 Aug 2012 19:34:34 +0900 Subject: [PATCH 029/133] step 23-A remove unused method Change-Id: I382c654fd30aca1a3277a186ee25fc946449c639 --- native/jni/src/gesture/gesture_decoder_wrapper.h | 7 ------- native/jni/src/gesture/incremental_decoder_interface.h | 1 - native/jni/src/gesture/incremental_decoder_wrapper.h | 7 ------- 3 files changed, 15 deletions(-) diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index 14a0705f7..2b9054f70 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -48,13 +48,6 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } - void reset() { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->reset(); - } - void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) { if (!mIncrementalDecoderInterface) { diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index cc219bf50..3951514c0 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -32,7 +32,6 @@ class IncrementalDecoderInterface { int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) = 0; - virtual void reset() = 0; virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) = 0; virtual ~IncrementalDecoderInterface() { }; diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 2f9602853..477b2cc8a 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -48,13 +48,6 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } - void reset() { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->reset(); - } - void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) { if (!mIncrementalDecoderInterface) { From 0f4de30e589deba221b92b3bde341c54fd9d1b9d Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 10 Aug 2012 20:12:56 +0900 Subject: [PATCH 030/133] Fix a bug on jni of DicTraverseSession Change-Id: Ieeac36cbad9a99d15911f2e6e2e841cae38ecb9b --- native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index f0fa2db2a..827e9f4aa 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -27,7 +27,7 @@ static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring return reinterpret_cast(traverseSession); } -static void latinime_initDicTraverseSession(JNIEnv *env, jlong traverseSession, +static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession, jlong dictionary, jintArray previousWord, jint previousWordLength) { void *ts = reinterpret_cast(traverseSession); Dictionary *dict = reinterpret_cast(dictionary); From f6c2200890012708709964fb1a8933ed1c3e7dd8 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 10 Aug 2012 21:04:18 +0900 Subject: [PATCH 031/133] Workaround to stop init dict traverse session Change-Id: I3d46c54a96a9fc0359ef75b9d2f8590b44164a8b --- .../com/android/inputmethod/latin/DicTraverseSession.java | 8 ++++---- .../com_android_inputmethod_latin_DicTraverseSession.cpp | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 160752896..4c3654213 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -23,8 +23,8 @@ public class DicTraverseSession { JniUtils.loadNativeLibrary(); } private native long setDicTraverseSessionNative(String locale); - private native void initDicTraverseSessionNative(long nativeDicTraverseSession, - long dictionary, int[] previousWord, int previwousWordLength); + //private native void initDicTraverseSessionNative(long nativeDicTraverseSession, + //long dictionary, int[] previousWord, int previousWordLength); private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); private long mNativeDicTraverseSession; @@ -43,8 +43,8 @@ public class DicTraverseSession { } public void initSession(long dictionary, int[] previousWord, int previousWordLength) { - initDicTraverseSessionNative( - mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); + //initDicTraverseSessionNative( + //mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); } private final long createNativeDicTraverseSession(String locale) { diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 827e9f4aa..6ff953874 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -22,11 +22,13 @@ #include "jni_common.h" namespace latinime { +class Dictionary; static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring localeJStr) { void *traverseSession = DicTraverseWrapper::getDicTraverseSession(env, localeJStr); return reinterpret_cast(traverseSession); } +#if 0 static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession, jlong dictionary, jintArray previousWord, jint previousWordLength) { void *ts = reinterpret_cast(traverseSession); @@ -35,6 +37,7 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong t env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength); } +#endif static void latinime_DicTraverseSession_release( JNIEnv *env, jobject object, jlong traverseSession) { @@ -44,7 +47,7 @@ static void latinime_DicTraverseSession_release( static JNINativeMethod sMethods[] = { {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, - {"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, + //{"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_DicTraverseSession_release} }; From f3850e554389dc3012584f9d81a4f2d3d4c89e44 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 10 Aug 2012 17:16:39 +0900 Subject: [PATCH 032/133] Use JNI Region calls also in getSuggestions() Change-Id: I9cc0208397c202aeb6f7b5bb5ad59e00c5afbd80 --- .../inputmethod/latin/BinaryDictionary.java | 18 ++- ...oid_inputmethod_latin_BinaryDictionary.cpp | 105 +++++++++++------- native/jni/src/correction.cpp | 2 - 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 096764499..9b85fb65a 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -51,7 +51,8 @@ public class BinaryDictionary extends Dictionary { private static final int TYPED_LETTER_MULTIPLIER = 2; private long mNativeDict; - private final int[] mInputCodes = new int[MAX_WORD_LENGTH]; + private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; + // TODO: The below should be int[] mOutputCodePoints private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS]; private final int[] mSpaceIndices = new int[MAX_SPACES]; private final int[] mOutputScores = new int[MAX_RESULTS]; @@ -92,7 +93,7 @@ public class BinaryDictionary extends Dictionary { private native boolean isValidBigramNative(long dict, int[] word1, int[] word2); private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, - int[] inputCodes, int codesSize, int commitPoint, boolean isGesture, + int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture, int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars, int[] outputScores, int[] outputIndices, int[] outputTypes); private static native float calcNormalizedScoreNative(char[] before, char[] after, int score); @@ -108,9 +109,7 @@ public class BinaryDictionary extends Dictionary { public ArrayList getSuggestions(final WordComposer composer, final CharSequence prevWord, final ProximityInfo proximityInfo) { if (!isValidDictionary()) return null; - Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE); - Arrays.fill(mOutputChars, (char) 0); - Arrays.fill(mOutputScores, 0); + Arrays.fill(mInputCodePoints, WordComposer.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWord) ? null : StringUtils.toCodePointArray(prevWord.toString()); @@ -120,7 +119,7 @@ public class BinaryDictionary extends Dictionary { if (composerSize <= 1 || !isGesture) { if (composerSize > MAX_WORD_LENGTH - 1) return null; for (int i = 0; i < composerSize; i++) { - mInputCodes[i] = composer.getCodeAt(i); + mInputCodePoints[i] = composer.getCodeAt(i); } } @@ -128,10 +127,9 @@ public class BinaryDictionary extends Dictionary { final int codesSize = isGesture ? ips.getPointerSize() : composerSize; // proximityInfo and/or prevWordForBigrams may not be null. final int tmpCount = getSuggestionsNative(mNativeDict, - proximityInfo.getNativeProximityInfo(), - mDicTraverseSession.getSession(), ips.getXCoordinates(), - ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), - mInputCodes, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray, + proximityInfo.getNativeProximityInfo(), mDicTraverseSession.getSession(), + ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), + mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray, mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); final int count = Math.min(tmpCount, MAX_PREDICTIONS); diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 1596a636d..876b43958 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -14,15 +14,12 @@ * limitations under the License. */ + +#include // for memset() + #define LOG_TAG "LatinIME: jni: BinaryDictionary" -#include "binary_format.h" -#include "com_android_inputmethod_latin_BinaryDictionary.h" -#include "correction.h" -#include "defines.h" -#include "dictionary.h" -#include "jni.h" -#include "jni_common.h" +#include "defines.h" // for macros below #ifdef USE_MMAP_FOR_DICTIONARY #include @@ -32,6 +29,13 @@ #include #endif // USE_MMAP_FOR_DICTIONARY +#include "binary_format.h" +#include "com_android_inputmethod_latin_BinaryDictionary.h" +#include "correction.h" +#include "dictionary.h" +#include "jni.h" +#include "jni_common.h" + namespace latinime { class ProximityInfo; @@ -124,50 +128,71 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jlong dict, jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, - jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdArray, - jintArray inputArray, jint arraySize, jint commitPoint, jboolean isGesture, - jintArray prevWordForBigrams, jboolean useFullEditDistance, jcharArray outputArray, - jintArray frequencyArray, jintArray spaceIndexArray, jintArray outputTypesArray) { + jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, + jintArray inputCodePointsArray, jint arraySize, jint commitPoint, jboolean isGesture, + jintArray prevWordCodePointsForBigrams, jboolean useFullEditDistance, + jcharArray outputCharsArray, jintArray scoresArray, jintArray spaceIndicesArray, + jintArray outputTypesArray) { Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return 0; ProximityInfo *pInfo = reinterpret_cast(proximityInfo); void *traverseSession = reinterpret_cast(dicTraverseSession); - int *xCoordinates = env->GetIntArrayElements(xCoordinatesArray, 0); - int *yCoordinates = env->GetIntArrayElements(yCoordinatesArray, 0); - int *times = env->GetIntArrayElements(timesArray, 0); - int *pointerIds = env->GetIntArrayElements(pointerIdArray, 0); - int *frequencies = env->GetIntArrayElements(frequencyArray, 0); - int *inputCodes = env->GetIntArrayElements(inputArray, 0); - jchar *outputChars = env->GetCharArrayElements(outputArray, 0); - int *spaceIndices = env->GetIntArrayElements(spaceIndexArray, 0); - int *outputTypes = env->GetIntArrayElements(outputTypesArray, 0); - jint *prevWordChars = prevWordForBigrams - ? env->GetIntArrayElements(prevWordForBigrams, 0) : 0; - jsize prevWordLength = prevWordChars ? env->GetArrayLength(prevWordForBigrams) : 0; + + // Input values + int xCoordinates[arraySize]; + int yCoordinates[arraySize]; + int times[arraySize]; + int pointerIds[arraySize]; + const jsize inputCodePointsLength = env->GetArrayLength(inputCodePointsArray); + int inputCodePoints[inputCodePointsLength]; + const jsize prevWordCodePointsLength = + prevWordCodePointsForBigrams ? env->GetArrayLength(prevWordCodePointsForBigrams) : 0; + int prevWordCodePointsInternal[prevWordCodePointsLength]; + int *prevWordCodePoints = 0; + env->GetIntArrayRegion(xCoordinatesArray, 0, arraySize, xCoordinates); + env->GetIntArrayRegion(yCoordinatesArray, 0, arraySize, yCoordinates); + env->GetIntArrayRegion(timesArray, 0, arraySize, times); + env->GetIntArrayRegion(pointerIdsArray, 0, arraySize, pointerIds); + env->GetIntArrayRegion(inputCodePointsArray, 0, inputCodePointsLength, inputCodePoints); + if (prevWordCodePointsForBigrams) { + env->GetIntArrayRegion(prevWordCodePointsForBigrams, 0, prevWordCodePointsLength, + prevWordCodePointsInternal); + prevWordCodePoints = prevWordCodePointsInternal; + } + + // Output values + // TODO: Should be "outputCodePointsLength" and "int outputCodePoints[]" + const jsize outputCharsLength = env->GetArrayLength(outputCharsArray); + unsigned short outputChars[outputCharsLength]; + const jsize scoresLength = env->GetArrayLength(scoresArray); + int scores[scoresLength]; + const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray); + int spaceIndices[spaceIndicesLength]; + const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); + int outputTypes[outputTypesLength]; + memset(outputChars, 0, outputCharsLength); + memset(scores, 0, scoresLength); + memset(spaceIndices, 0, spaceIndicesLength); + memset(outputTypes, 0, outputTypesLength); int count; if (isGesture || arraySize > 1) { count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, - times, pointerIds, inputCodes, arraySize, prevWordChars, prevWordLength, - commitPoint, isGesture, useFullEditDistance, (unsigned short*) outputChars, - frequencies, spaceIndices, outputTypes); + times, pointerIds, inputCodePoints, arraySize, prevWordCodePoints, + prevWordCodePointsLength, commitPoint, isGesture, useFullEditDistance, outputChars, + scores, spaceIndices, outputTypes); } else { - count = dictionary->getBigrams(prevWordChars, prevWordLength, inputCodes, - arraySize, (unsigned short*) outputChars, frequencies, outputTypes); + count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength, + inputCodePoints, arraySize, outputChars, scores, outputTypes); } - if (prevWordChars) { - env->ReleaseIntArrayElements(prevWordForBigrams, prevWordChars, JNI_ABORT); - } - env->ReleaseIntArrayElements(outputTypesArray, outputTypes, 0); - env->ReleaseIntArrayElements(spaceIndexArray, spaceIndices, 0); - env->ReleaseCharArrayElements(outputArray, outputChars, 0); - env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); - env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); - env->ReleaseIntArrayElements(pointerIdArray, pointerIds, 0); - env->ReleaseIntArrayElements(timesArray, times, 0); - env->ReleaseIntArrayElements(yCoordinatesArray, yCoordinates, 0); - env->ReleaseIntArrayElements(xCoordinatesArray, xCoordinates, 0); + // Copy back the output values + // TODO: Should be SetIntArrayRegion() + env->SetCharArrayRegion(outputCharsArray, 0, outputCharsLength, outputChars); + env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores); + env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices); + env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes); + return count; } diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index ea4bddae2..b18b35e8c 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -154,8 +154,6 @@ void Correction::checkState() { if (mSkipPos >= 0) ++inputCount; if (mExcessivePos >= 0) ++inputCount; if (mTransposedPos >= 0) ++inputCount; - // TODO: remove this assert - assert(inputCount <= 1); } } From 063c3e2171e546957d8c40575740c29f234e307c Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 10 Aug 2012 22:10:04 +0900 Subject: [PATCH 033/133] Fix memset() bugs Change-Id: Icf4341c0a62e782cf8c2a863408591df4e6f07cb --- .../com_android_inputmethod_latin_BinaryDictionary.cpp | 8 ++++---- native/jni/src/proximity_info.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 876b43958..7e1e0e424 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -170,10 +170,10 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, int spaceIndices[spaceIndicesLength]; const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); int outputTypes[outputTypesLength]; - memset(outputChars, 0, outputCharsLength); - memset(scores, 0, scoresLength); - memset(spaceIndices, 0, spaceIndicesLength); - memset(outputTypes, 0, outputTypesLength); + memset(outputChars, 0, outputCharsLength * sizeof(outputChars[0])); + memset(scores, 0, scoresLength * sizeof(scores[0])); + memset(spaceIndices, 0, spaceIndicesLength * sizeof(spaceIndices[0])); + memset(outputTypes, 0, outputTypesLength * sizeof(outputTypes[0])); int count; if (isGesture || arraySize > 1) { diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 2633913f7..18a0b74a8 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -34,7 +34,7 @@ static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray if (jArray && buffer) { env->GetIntArrayRegion(jArray, 0, len, buffer); } else if (buffer) { - memset(buffer, 0, len); + memset(buffer, 0, len * sizeof(jint)); } } @@ -43,7 +43,7 @@ static inline void safeGetOrFillZeroFloatArrayRegion(JNIEnv *env, jfloatArray jA if (jArray && buffer) { env->GetFloatArrayRegion(jArray, 0, len, buffer); } else if (buffer) { - memset(buffer, 0, len); + memset(buffer, 0, len * sizeof(jfloat)); } } From 9b6b2798078d0b179af35a2cde894e19dbd11a05 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Fri, 10 Aug 2012 11:50:11 -0700 Subject: [PATCH 034/133] Import translations. DO NOT MERGE Change-Id: I22eacc83a5aaa0f4e50e02e84ebf2b176dd08fe6 Auto-generated-cl: translation import --- java/res/values-hi/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 254c2d29b..56642c478 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -46,9 +46,9 @@ "एड-ऑन डिक्शनरी" "मुख्‍य डिक्‍शनरी" "सुधार सुझाव दिखाएं" - "लिखते समय सुझाए गए शब्‍द प्रदर्शित करें" + "लिखते समय सुझाए गए शब्‍द दिखाएं" "हमेशा दिखाएं" - "र्पोट्रेट मोड पर प्रदर्शित करें" + "र्पोट्रेट मोड पर दिखाएं" "हमेशा छुपाएं" "स्‍वत: सुधार" "Spacebar और विराम चिह्न गलत लिखे गए शब्‍दों को स्‍वचालित रूप से ठीक करते हैं" From 5f312c9c1546da9f73d02f911d3365da4ff658fb Mon Sep 17 00:00:00 2001 From: alanv Date: Fri, 10 Aug 2012 13:51:17 -0700 Subject: [PATCH 035/133] Change accessibility event type for app-driven speech. Bug: 6614400 Change-Id: I0ef309b7f3f35bf37b4664ea599c9e6ffb1a4ab8 --- .../accessibility/AccessibilityUtils.java | 33 +++++++++++++++---- .../AccessibleKeyboardViewProxy.java | 4 +-- .../android/inputmethod/latin/LatinIME.java | 2 +- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index 616b1c6d7..58d3022c9 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -19,10 +19,15 @@ package com.android.inputmethod.accessibility; import android.content.Context; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; +import android.os.Build; import android.os.SystemClock; import android.provider.Settings; +import android.support.v4.view.accessibility.AccessibilityEventCompat; import android.util.Log; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.EditorInfo; @@ -138,9 +143,10 @@ public class AccessibilityUtils { * Sends the specified text to the {@link AccessibilityManager} to be * spoken. * - * @param text the text to speak + * @param view The source view. + * @param text The text to speak. */ - public void speak(CharSequence text) { + public void announceForAccessibility(View view, CharSequence text) { if (!mAccessibilityManager.isEnabled()) { Log.e(TAG, "Attempted to speak when accessibility was disabled!"); return; @@ -149,8 +155,7 @@ public class AccessibilityUtils { // The following is a hack to avoid using the heavy-weight TextToSpeech // class. Instead, we're just forcing a fake AccessibilityEvent into // the screen reader to make it speak. - final AccessibilityEvent event = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); + final AccessibilityEvent event = AccessibilityEvent.obtain(); event.setPackageName(PACKAGE); event.setClassName(CLASS); @@ -158,20 +163,34 @@ public class AccessibilityUtils { event.setEnabled(true); event.getText().add(text); - mAccessibilityManager.sendAccessibilityEvent(event); + // Platforms starting at SDK 16 should use announce events. + if (Build.VERSION.SDK_INT >= 16) { + event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT); + } else { + event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + + final ViewParent viewParent = view.getParent(); + if ((viewParent == null) || !(viewParent instanceof ViewGroup)) { + Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility"); + return; + } + + viewParent.requestSendAccessibilityEvent(view, event); } /** * Handles speaking the "connect a headset to hear passwords" notification * when connecting to a password field. * + * @param view The source view. * @param editorInfo The input connection's editor info attribute. * @param restarting Whether the connection is being restarted. */ - public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) { + public void onStartInputViewInternal(View view, EditorInfo editorInfo, boolean restarting) { if (shouldObscureInput(editorInfo)) { final CharSequence text = mContext.getText(R.string.spoken_use_headphones); - speak(text); + announceForAccessibility(view, text); } } diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 4ecbf827a..2fff73154 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -250,7 +250,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { text = context.getText(R.string.spoken_description_shiftmode_off); } - AccessibilityUtils.getInstance().speak(text); + AccessibilityUtils.getInstance().announceForAccessibility(mView, text); } /** @@ -290,6 +290,6 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { } final String text = context.getString(resId); - AccessibilityUtils.getInstance().speak(text); + AccessibilityUtils.getInstance().announceForAccessibility(mView, text); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 455086015..25cddb22d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -663,7 +663,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Forward this event to the accessibility utilities, if enabled. final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance(); if (accessUtils.isTouchExplorationEnabled()) { - accessUtils.onStartInputViewInternal(editorInfo, restarting); + accessUtils.onStartInputViewInternal(inputView, editorInfo, restarting); } if (!restarting) { From 847c8dfe17c31c88d869ae469194edf574c9a474 Mon Sep 17 00:00:00 2001 From: satok Date: Sat, 11 Aug 2012 01:10:31 +0900 Subject: [PATCH 036/133] Fix JNI Change-Id: If134baf52e78fbe18b78611a23870f5d10c80955 --- java/proguard.flags | 4 ++++ .../android/inputmethod/latin/BinaryDictionary.java | 1 + .../inputmethod/latin/DicTraverseSession.java | 8 ++++---- ..._android_inputmethod_latin_DicTraverseSession.cpp | 12 +++++++----- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/java/proguard.flags b/java/proguard.flags index 24b4c1987..ac5b7df16 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -44,6 +44,10 @@ (...); } +-keepclasseswithmembernames class * { + native ; +} + -keep class com.android.inputmethod.research.ResearchLogger { void flush(); void publishCurrentLogUnit(...); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 9b85fb65a..f0f5cd320 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -79,6 +79,7 @@ public class BinaryDictionary extends Dictionary { mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length); mDicTraverseSession = new DicTraverseSession(locale); + mDicTraverseSession.initSession(mNativeDict); } static { diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 4c3654213..c76815363 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -23,8 +23,8 @@ public class DicTraverseSession { JniUtils.loadNativeLibrary(); } private native long setDicTraverseSessionNative(String locale); - //private native void initDicTraverseSessionNative(long nativeDicTraverseSession, - //long dictionary, int[] previousWord, int previousWordLength); + private native void initDicTraverseSessionNative(long nativeDicTraverseSession, + long dictionary, int[] previousWord, int previousWordLength); private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession); private long mNativeDicTraverseSession; @@ -43,8 +43,8 @@ public class DicTraverseSession { } public void initSession(long dictionary, int[] previousWord, int previousWordLength) { - //initDicTraverseSessionNative( - //mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); + initDicTraverseSessionNative( + mNativeDicTraverseSession, dictionary, previousWord, previousWordLength); } private final long createNativeDicTraverseSession(String locale) { diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 6ff953874..806ff1f1d 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -28,18 +28,20 @@ static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring return reinterpret_cast(traverseSession); } -#if 0 static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession, jlong dictionary, jintArray previousWord, jint previousWordLength) { void *ts = reinterpret_cast(traverseSession); Dictionary *dict = reinterpret_cast(dictionary); + if (!previousWord) { + DicTraverseWrapper::initDicTraverseSession(ts, dict, 0, 0); + return; + } int prevWord[previousWordLength]; env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength); } -#endif -static void latinime_DicTraverseSession_release( +static void latinime_releaseDicTraverseSession( JNIEnv *env, jobject object, jlong traverseSession) { void *ts = reinterpret_cast(traverseSession); DicTraverseWrapper::releaseDicTraverseSession(ts); @@ -47,8 +49,8 @@ static void latinime_DicTraverseSession_release( static JNINativeMethod sMethods[] = { {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, - //{"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, - {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_DicTraverseSession_release} + {"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, + {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_releaseDicTraverseSession} }; int register_DicTraverseSession(JNIEnv *env) { From bcec82de66f52655593dc233346f11468f5077a0 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sun, 12 Aug 2012 11:10:48 +0900 Subject: [PATCH 037/133] Clean up constructors And, use C++ style casts and use float math functions rather than double ones to save memory space. Also, stop using FloatMath and NativeUtils as standard Math methods are faster now. See http://code.google.com/p/android/issues/detail?id=36199 and https://android-review.googlesource.com/40700 multi-project commit with I4259fb5ab8a15ac5760a7f04fc8f4c860529f04a Change-Id: I0b81cff8c91769f7559a59b9528c75a5aabb4211 --- .../inputmethod/keyboard/ProximityInfo.java | 5 +- .../keyboard/internal/GestureStroke.java | 8 +-- .../inputmethod/latin/NativeUtils.java | 32 ---------- .../UserHistoryForgettingCurveUtils.java | 2 +- native/jni/Android.mk | 1 - ...oid_inputmethod_keyboard_ProximityInfo.cpp | 4 +- ...oid_inputmethod_latin_BinaryDictionary.cpp | 39 ++++++++----- ...d_inputmethod_latin_DicTraverseSession.cpp | 12 ++-- ..._android_inputmethod_latin_NativeUtils.cpp | 38 ------------ ...om_android_inputmethod_latin_NativeUtils.h | 27 --------- native/jni/jni_common.cpp | 6 -- native/jni/src/bigram_dictionary.cpp | 13 +++-- native/jni/src/char_utils.cpp | 9 +-- native/jni/src/correction.cpp | 8 +-- native/jni/src/debug.h | 7 ++- native/jni/src/dictionary.cpp | 4 +- native/jni/src/dictionary.h | 11 +++- native/jni/src/geometry_utils.h | 58 ++++++++++--------- .../jni/src/gesture/gesture_decoder_wrapper.h | 2 +- .../gesture/incremental_decoder_interface.h | 3 +- .../src/gesture/incremental_decoder_wrapper.h | 2 +- native/jni/src/proximity_info.cpp | 4 +- native/jni/src/words_priority_queue.h | 9 +-- 23 files changed, 114 insertions(+), 190 deletions(-) delete mode 100644 java/src/com/android/inputmethod/latin/NativeUtils.java delete mode 100644 native/jni/com_android_inputmethod_latin_NativeUtils.cpp delete mode 100644 native/jni/com_android_inputmethod_latin_NativeUtils.h diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index ae123e29a..ac0a56ba3 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard; import android.graphics.Rect; import android.text.TextUtils; -import android.util.FloatMath; import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection; import com.android.inputmethod.latin.JniUtils; @@ -155,7 +154,9 @@ public class ProximityInfo { final float radius = touchPositionCorrection.mRadii[row]; sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth; sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight; - sweetSpotRadii[i] = radius * FloatMath.sqrt( + // 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. + sweetSpotRadii[i] = radius * (float)Math.sqrt( hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 28d6c1d07..5ccb40f5d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -16,7 +16,6 @@ package com.android.inputmethod.keyboard.internal; import android.graphics.Canvas; import android.graphics.Paint; -import android.util.FloatMath; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; @@ -50,7 +49,7 @@ public class GestureStroke { private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f); - private static final float DOUBLE_PI = (float)(2 * Math.PI); + private static final float DOUBLE_PI = (float)(2.0f * Math.PI); // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT private static final int DRAWING_GESTURE_FADE_START = 10; @@ -158,8 +157,9 @@ public class GestureStroke { final int p2x, final int p2y) { final float dx = p1x - p2x; final float dy = p1y - p2y; - // TODO: Optimize out this {@link FloatMath#sqrt(float)} call. - return FloatMath.sqrt(dx * dx + dy * dy); + // 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); } private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) { diff --git a/java/src/com/android/inputmethod/latin/NativeUtils.java b/java/src/com/android/inputmethod/latin/NativeUtils.java deleted file mode 100644 index 9cc2bc02e..000000000 --- a/java/src/com/android/inputmethod/latin/NativeUtils.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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; - -public class NativeUtils { - static { - JniUtils.loadNativeLibrary(); - } - - private NativeUtils() { - // This utility class is not publicly instantiable. - } - - /** - * This method just calls up libm's powf() directly. - */ - public static native float powf(float x, float y); -} diff --git a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java index 1de95d7b8..5a2fdf48e 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java @@ -212,7 +212,7 @@ public class UserHistoryForgettingCurveUtils { for (int j = 0; j < ELAPSED_TIME_MAX; ++j) { final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS; final float freq = initialFreq - * NativeUtils.powf(initialFreq, elapsedHours / HALF_LIFE_HOURS); + * (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS); final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq)); SCORE_TABLE[i][j] = intFreq; } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 86ad8576e..1725a7638 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -37,7 +37,6 @@ LATIN_IME_JNI_SRC_FILES := \ com_android_inputmethod_keyboard_ProximityInfo.cpp \ com_android_inputmethod_latin_BinaryDictionary.cpp \ com_android_inputmethod_latin_DicTraverseSession.cpp \ - com_android_inputmethod_latin_NativeUtils.cpp \ jni_common.cpp LATIN_IME_CORE_SRC_FILES := \ diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index f9b23f06d..545d91a70 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -43,8 +43,8 @@ static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximi static JNINativeMethod sKeyboardMethods[] = { {"setProximityInfoNative", "(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J", - (void*)latinime_Keyboard_setProximityInfo}, - {"releaseProximityInfoNative", "(J)V", (void*)latinime_Keyboard_release} + reinterpret_cast(latinime_Keyboard_setProximityInfo)}, + {"releaseProximityInfoNative", "(J)V", reinterpret_cast(latinime_Keyboard_release)} }; int register_ProximityInfo(JNIEnv *env) { diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 7e1e0e424..5a287a122 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -75,7 +75,7 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); return 0; } - dictBuf = (void *)((char *)dictBuf + adjust); + dictBuf = reinterpret_cast(reinterpret_cast(dictBuf) + adjust); #else // USE_MMAP_FOR_DICTIONARY /* malloc version */ FILE *file = 0; @@ -110,10 +110,11 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, return 0; } Dictionary *dictionary = 0; - if (BinaryFormat::UNKNOWN_FORMAT == BinaryFormat::detectFormat((uint8_t*)dictBuf)) { + if (BinaryFormat::UNKNOWN_FORMAT + == BinaryFormat::detectFormat(reinterpret_cast(dictBuf))) { AKLOGE("DICT: dictionary format is unknown, bad magic number"); #ifdef USE_MMAP_FOR_DICTIONARY - releaseDictBuf(((char*)dictBuf) - adjust, adjDictSize, fd); + releaseDictBuf(reinterpret_cast(dictBuf) - adjust, adjDictSize, fd); #else // USE_MMAP_FOR_DICTIONARY releaseDictBuf(dictBuf, 0, 0); #endif // USE_MMAP_FOR_DICTIONARY @@ -227,8 +228,9 @@ static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject jchar afterChars[afterLength]; env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); env->GetCharArrayRegion(after, 0, afterLength, afterChars); - return Correction::RankingAlgorithm::calcNormalizedScore((unsigned short*)beforeChars, - beforeLength, (unsigned short*)afterChars, afterLength, score); + return Correction::RankingAlgorithm::calcNormalizedScore( + reinterpret_cast(beforeChars), beforeLength, + reinterpret_cast(afterChars), afterLength, score); } static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, @@ -239,8 +241,9 @@ static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, jchar afterChars[afterLength]; env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); env->GetCharArrayRegion(after, 0, afterLength, afterChars); - return Correction::RankingAlgorithm::editDistance((unsigned short*)beforeChars, beforeLength, - (unsigned short*)afterChars, afterLength); + return Correction::RankingAlgorithm::editDistance( + reinterpret_cast(beforeChars), beforeLength, + reinterpret_cast(afterChars), afterLength); } static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) { @@ -249,7 +252,9 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong d void *dictBuf = dictionary->getDict(); if (!dictBuf) return; #ifdef USE_MMAP_FOR_DICTIONARY - releaseDictBuf((void *)((char *)dictBuf - dictionary->getDictBufAdjust()), + releaseDictBuf( + reinterpret_cast( + reinterpret_cast(dictBuf) - dictionary->getDictBufAdjust()), dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd()); #else // USE_MMAP_FOR_DICTIONARY releaseDictBuf(dictBuf, 0, 0); @@ -273,15 +278,19 @@ static void releaseDictBuf(void *dictBuf, const size_t length, int fd) { } static JNINativeMethod sMethods[] = { - {"openNative", "(Ljava/lang/String;JJIIIII)J", (void*)latinime_BinaryDictionary_open}, - {"closeNative", "(J)V", (void*)latinime_BinaryDictionary_close}, + {"openNative", "(Ljava/lang/String;JJIIIII)J", + reinterpret_cast(latinime_BinaryDictionary_open)}, + {"closeNative", "(J)V", reinterpret_cast(latinime_BinaryDictionary_close)}, {"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I", - (void*) latinime_BinaryDictionary_getSuggestions}, - {"getFrequencyNative", "(J[I)I", (void*)latinime_BinaryDictionary_getFrequency}, - {"isValidBigramNative", "(J[I[I)Z", (void*)latinime_BinaryDictionary_isValidBigram}, + reinterpret_cast(latinime_BinaryDictionary_getSuggestions)}, + {"getFrequencyNative", "(J[I)I", + reinterpret_cast(latinime_BinaryDictionary_getFrequency)}, + {"isValidBigramNative", "(J[I[I)Z", + reinterpret_cast(latinime_BinaryDictionary_isValidBigram)}, {"calcNormalizedScoreNative", "([C[CI)F", - (void*)latinime_BinaryDictionary_calcNormalizedScore}, - {"editDistanceNative", "([C[C)I", (void*)latinime_BinaryDictionary_editDistance} + reinterpret_cast(latinime_BinaryDictionary_calcNormalizedScore)}, + {"editDistanceNative", "([C[C)I", + reinterpret_cast(latinime_BinaryDictionary_editDistance)} }; int register_BinaryDictionary(JNIEnv *env) { diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 806ff1f1d..947360e8e 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -41,16 +41,18 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong t DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength); } -static void latinime_releaseDicTraverseSession( - JNIEnv *env, jobject object, jlong traverseSession) { +static void latinime_releaseDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession) { void *ts = reinterpret_cast(traverseSession); DicTraverseWrapper::releaseDicTraverseSession(ts); } static JNINativeMethod sMethods[] = { - {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", (void*)latinime_setDicTraverseSession}, - {"initDicTraverseSessionNative", "(JJ[II)V", (void*)latinime_initDicTraverseSession}, - {"releaseDicTraverseSessionNative", "(J)V", (void*)latinime_releaseDicTraverseSession} + {"setDicTraverseSessionNative", "(Ljava/lang/String;)J", + reinterpret_cast(latinime_setDicTraverseSession)}, + {"initDicTraverseSessionNative", "(JJ[II)V", + reinterpret_cast(latinime_initDicTraverseSession)}, + {"releaseDicTraverseSessionNative", "(J)V", + reinterpret_cast(latinime_releaseDicTraverseSession)} }; int register_DicTraverseSession(JNIEnv *env) { diff --git a/native/jni/com_android_inputmethod_latin_NativeUtils.cpp b/native/jni/com_android_inputmethod_latin_NativeUtils.cpp deleted file mode 100644 index 8f1afbeb6..000000000 --- a/native/jni/com_android_inputmethod_latin_NativeUtils.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -#include "com_android_inputmethod_latin_NativeUtils.h" -#include "jni.h" -#include "jni_common.h" - -#include - -namespace latinime { - -static float latinime_NativeUtils_powf(float x, float y) { - return powf(x, y); -} - -static JNINativeMethod sMethods[] = { - {"powf", "(FF)F", (void*)latinime_NativeUtils_powf} -}; - -int register_NativeUtils(JNIEnv *env) { - const char *const kClassPathName = "com/android/inputmethod/latin/NativeUtils"; - return registerNativeMethods(env, kClassPathName, sMethods, - sizeof(sMethods) / sizeof(sMethods[0])); -} -} // namespace latinime diff --git a/native/jni/com_android_inputmethod_latin_NativeUtils.h b/native/jni/com_android_inputmethod_latin_NativeUtils.h deleted file mode 100644 index d1ffb8f4a..000000000 --- a/native/jni/com_android_inputmethod_latin_NativeUtils.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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. - */ - -#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H -#define _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H - -#include "jni.h" - -namespace latinime { - -int register_NativeUtils(JNIEnv *env); - -} // namespace latinime -#endif // _COM_ANDROID_INPUTMETHOD_LATIN_NATIVEUTILS_H diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp index 795262aa4..1586f253b 100644 --- a/native/jni/jni_common.cpp +++ b/native/jni/jni_common.cpp @@ -21,7 +21,6 @@ #include "com_android_inputmethod_keyboard_ProximityInfo.h" #include "com_android_inputmethod_latin_BinaryDictionary.h" #include "com_android_inputmethod_latin_DicTraverseSession.h" -#include "com_android_inputmethod_latin_NativeUtils.h" #include "defines.h" #include "jni.h" #include "jni_common.h" @@ -56,11 +55,6 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { goto bail; } - if (!register_NativeUtils(env)) { - AKLOGE("ERROR: NativeUtils native registration failed"); - goto bail; - } - /* success -- return valid version number */ result = JNI_VERSION_1_6; diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp index 220171127..df1ebc0e3 100644 --- a/native/jni/src/bigram_dictionary.cpp +++ b/native/jni/src/bigram_dictionary.cpp @@ -60,14 +60,15 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ AKLOGI("Bigram: InsertAt -> %d MAX_PREDICTIONS: %d", insertAt, MAX_PREDICTIONS); } if (insertAt < MAX_PREDICTIONS) { - memmove((char*) bigramFreq + (insertAt + 1) * sizeof(bigramFreq[0]), - (char*) bigramFreq + insertAt * sizeof(bigramFreq[0]), - (MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramFreq[0])); + memmove(reinterpret_cast(bigramFreq) + (insertAt + 1) * sizeof(bigramFreq[0]), + reinterpret_cast(bigramFreq) + insertAt * sizeof(bigramFreq[0]), + (MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramFreq[0])); bigramFreq[insertAt] = frequency; outputTypes[insertAt] = Dictionary::KIND_PREDICTION; - memmove((char*) bigramChars + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short), - (char*) bigramChars + (insertAt ) * MAX_WORD_LENGTH * sizeof(short), - (MAX_PREDICTIONS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH); + memmove(reinterpret_cast(bigramChars) + + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short), + reinterpret_cast(bigramChars) + insertAt * MAX_WORD_LENGTH * sizeof(short), + (MAX_PREDICTIONS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH); unsigned short *dest = bigramChars + (insertAt ) * MAX_WORD_LENGTH; while (length--) { *dest++ = *word++; diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp index 45d49b087..fc0a0596f 100644 --- a/native/jni/src/char_utils.cpp +++ b/native/jni/src/char_utils.cpp @@ -885,16 +885,17 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { }; static int compare_pair_capital(const void *a, const void *b) { - return (int)(*(unsigned short *)a) - - (int)((struct LatinCapitalSmallPair*)b)->capital; + return static_cast(*reinterpret_cast(a)) + - static_cast( + (reinterpret_cast(b))->capital); } unsigned short latin_tolower(unsigned short c) { struct LatinCapitalSmallPair *p = - (struct LatinCapitalSmallPair *)bsearch(&c, SORTED_CHAR_MAP, + reinterpret_cast(bsearch(&c, SORTED_CHAR_MAP, sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]), sizeof(SORTED_CHAR_MAP[0]), - compare_pair_capital); + compare_pair_capital)); return p ? p->small : c; } } // namespace latinime diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index b18b35e8c..e1f9db92d 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -1092,7 +1092,7 @@ int Correction::RankingAlgorithm::editDistance(const unsigned short *before, // In dictionary.cpp, getSuggestion() method, // suggestion scores are computed using the below formula. // original score -// := pow(mTypedLetterMultiplier (this is defined 2), +// := powf(mTypedLetterMultiplier (this is defined 2), // (the number of matched characters between typed word and suggested word)) // * (individual word's score which defined in the unigram dictionary, // and this score is defined in range [0, 255].) @@ -1104,11 +1104,11 @@ int Correction::RankingAlgorithm::editDistance(const unsigned short *before, // capitalization, then treat it as if the score was 255. // - If before.length() == after.length() // => multiply by mFullWordMultiplier (this is defined 2)) -// So, maximum original score is pow(2, min(before.length(), after.length())) * 255 * 2 * 1.2 +// So, maximum original score is powf(2, min(before.length(), after.length())) * 255 * 2 * 1.2 // For historical reasons we ignore the 1.2 modifier (because the measure for a good // autocorrection threshold was done at a time when it didn't exist). This doesn't change // the result. -// So, we can normalize original score by dividing pow(2, min(b.l(),a.l())) * 255 * 2. +// So, we can normalize original score by dividing powf(2, min(b.l(),a.l())) * 255 * 2. /* static */ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *before, @@ -1130,7 +1130,7 @@ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *be } const float maxScore = score >= S_INT_MAX ? S_INT_MAX : MAX_INITIAL_SCORE - * pow(static_cast(TYPED_LETTER_MULTIPLIER), + * powf(static_cast(TYPED_LETTER_MULTIPLIER), static_cast(min(beforeLength, afterLength - spaceCount))) * FULL_WORD_MULTIPLIER; diff --git a/native/jni/src/debug.h b/native/jni/src/debug.h index 2168d6672..2432b1f2e 100644 --- a/native/jni/src/debug.h +++ b/native/jni/src/debug.h @@ -58,11 +58,12 @@ static inline void LOGI_S16_PLUS(unsigned short *string, const unsigned int leng } static inline void printDebug(const char *tag, int *codes, int codesSize, int MAX_PROXIMITY_CHARS) { - unsigned char *buf = (unsigned char*)malloc((1 + codesSize) * sizeof(*buf)); + unsigned char *buf = reinterpret_cast(malloc((1 + codesSize) * sizeof(*buf))); buf[codesSize] = 0; - while (--codesSize >= 0) - buf[codesSize] = (unsigned char)codes[codesSize * MAX_PROXIMITY_CHARS]; + while (--codesSize >= 0) { + buf[codesSize] = static_cast(codes[codesSize * MAX_PROXIMITY_CHARS]); + } AKLOGI("%s, WORD = %s", tag, buf); free(buf); diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index f3bdb310d..75d566cc7 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -32,8 +32,8 @@ namespace latinime { Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions) - : mDict((unsigned char*) dict), - mOffsetDict(((unsigned char*) dict) + BinaryFormat::getHeaderSize(mDict)), + : mDict(reinterpret_cast(dict)), + mOffsetDict((reinterpret_cast(dict)) + BinaryFormat::getHeaderSize(mDict)), mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) { if (DEBUG_DICT) { if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) { diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h index b550ba4df..3b55e5de9 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/dictionary.h @@ -55,8 +55,12 @@ class Dictionary { int getFrequency(const int32_t *word, int length) const; bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const; - void *getDict() const { return (void *)mDict; } // required to release dictionary buffer - void *getOffsetDict() const { return (void *)mOffsetDict; } + void *getDict() const { // required to release dictionary buffer + return reinterpret_cast(const_cast(mDict)); + } + void *getOffsetDict() const { + return reinterpret_cast(const_cast(mOffsetDict)); + } int getDictSize() const { return mDictSize; } int getMmapFd() const { return mMmapFd; } int getDictBufAdjust() const { return mDictBufAdjust; } @@ -87,8 +91,9 @@ class Dictionary { inline int Dictionary::wideStrLen(unsigned short *str) { if (!str) return 0; unsigned short *end = str; - while (*end) + while (*end) { end++; + } return end - str; } } // namespace latinime diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h index ada889e11..65611b9ba 100644 --- a/native/jni/src/geometry_utils.h +++ b/native/jni/src/geometry_utils.h @@ -20,36 +20,43 @@ #include #define MAX_DISTANCE 10000000 -#define KEY_NUM 27 -#define SPACE_KEY 26 #define MAX_PATHS 2 #define DEBUG_DECODER false +#define M_PI_F 3.14159265f + namespace latinime { -static inline float sqr(float x) { +static inline float sqrf(float x) { return x * x; } -static inline float getNormalizedSqrDistance(int x1, int y1, int x2, int y2, int scale) { - return sqr((x1 - x2) * 1.0 / scale) + sqr((y1 - y2) * 1.0 / scale); +static inline float getNormalizedSqrDistanceFloat(int x1, int y1, int x2, int y2, int scale) { + return sqrf(static_cast(x1 - x2) / static_cast(scale)) + + sqrf(static_cast(y1 - y2) / static_cast(scale)); } -static inline int getDistance(int x1, int y1, int x2, int y2) { - return (int) sqrt(sqr(x2 - x1) + sqr(y2 - y1)); +static inline float getDistanceSqrFloat(float x1, float y1, float x2, float y2) { + return sqrf(x2 - x1) + sqrf(y2 - y1); } -static inline float getDistanceSq(float x1, float y1, float x2, float y2) { - return sqr(x2 - x1) + sqr(y2 - y1); +static inline int getDistanceInt(int x1, int y1, int x2, int y2) { + return static_cast( + sqrtf(getDistanceSqrFloat( + static_cast(x1), static_cast(y1), + static_cast(x2), static_cast(y2)))); } static inline float getAngle(int x1, int y1, int x2, int y2) { - float dx = x1 - x2; - float dy = y1 - y2; - if (dx == 0 && dy == 0) + int dx = x1 - x2; + int dy = y1 - y2; + if (dx == 0 && dy == 0) { return 0; - return atan2(dy, dx); + } + float dxf = static_cast(dx); + float dyf = static_cast(dy); + return atan2f(dyf, dxf); } static inline float angleDiff(float a1, float a2) { @@ -57,21 +64,22 @@ static inline float angleDiff(float a1, float a2) { if (diff < 0) { diff = -diff; } - if (diff > M_PI) { - return 2 * M_PI - diff; + if (diff > M_PI_F) { + return 2.0f * M_PI_F - diff; } return diff; } -//static float pointToLineDistanceSq(float x, float y, float x1, float y1, float x2, float y2) { -// float A = x - x1; -// float B = y - y1; -// float C = x2 - x1; -// float D = y2 - y1; -// return abs(A * D - C * B) / sqrt(C * C + D * D); -//} +// static float pointToLineDistanceSqrFloat( +// float x, float y, float x1, float y1, float x2, float y2) { +// float A = x - x1; +// float B = y - y1; +// float C = x2 - x1; +// float D = y2 - y1; +// return abs(A * D - C * B) / sqrt(C * C + D * D); +// } -static inline float pointToLineSegDistanceSq( +static inline float pointToLineSegDistanceSqrFloat( float x, float y, float x1, float y1, float x2, float y2) { float ray1x = x - x1; float ray1y = y - y1; @@ -93,9 +101,7 @@ static inline float pointToLineSegDistanceSq( projectionX = x1 + projectionLengthSq * ray2x; projectionY = y1 + projectionLengthSq * ray2y; } - - float dist = getDistanceSq(x, y, projectionX, projectionY); - return dist; + return getDistanceSqrFloat(x, y, projectionX, projectionY); } } // namespace latinime #endif // LATINIME_INCREMENTAL_GEOMETRY_UTILS_H diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index 2b9054f70..4a26d9c1b 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -62,7 +62,7 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { } private: - DISALLOW_COPY_AND_ASSIGN(GestureDecoderWrapper); + DISALLOW_IMPLICIT_CONSTRUCTORS(GestureDecoderWrapper); static IncrementalDecoderInterface *getGestureDecoderInstance(int maxWordLength, int maxWords) { if (sGestureDecoderFactoryMethod) { return sGestureDecoderFactoryMethod(maxWordLength, maxWords); diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index 3951514c0..e940344d4 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -34,9 +34,10 @@ class IncrementalDecoderInterface { int *outputIndices, int *outputTypes) = 0; virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, const uint8_t *dictRoot, int rootPos) = 0; + IncrementalDecoderInterface() { }; virtual ~IncrementalDecoderInterface() { }; private: - //DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderInterface); + DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderInterface); }; } // namespace latinime #endif // LATINIME_INCREMENTAL_DECODER_INTERFACE_H diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 477b2cc8a..f47d539b7 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -62,7 +62,7 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { } private: - DISALLOW_COPY_AND_ASSIGN(IncrementalDecoderWrapper); + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalDecoderWrapper); static IncrementalDecoderInterface *getIncrementalDecoderInstance(int maxWordLength, int maxWords) { if (sIncrementalDecoderFactoryMethod) { diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 18a0b74a8..ade78a1ec 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -248,7 +248,7 @@ void ProximityInfo::initializeG() { for (int i = 0; i < KEY_COUNT; i++) { mKeyKeyDistancesG[i][i] = 0; for (int j = i + 1; j < KEY_COUNT; j++) { - mKeyKeyDistancesG[i][j] = getDistance( + mKeyKeyDistancesG[i][j] = getDistanceInt( mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; } @@ -290,7 +290,7 @@ int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const { void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex, int *keyToCodeIndex, int *keyCount, int *keyWidth) const { *keyCount = KEY_COUNT; - *keyWidth = sqrt(static_cast(MOST_COMMON_KEY_WIDTH_SQUARE)); + *keyWidth = sqrtf(static_cast(MOST_COMMON_KEY_WIDTH_SQUARE)); for (int i = 0; i < KEY_COUNT; ++i) { const int code = mKeyCharCodes[i]; diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h index 8a6da1c95..e97e16a12 100644 --- a/native/jni/src/words_priority_queue.h +++ b/native/jni/src/words_priority_queue.h @@ -129,7 +129,7 @@ class WordsPriorityQueue { } } if (maxIndex > 0 && nsMaxSw) { - memmove(&swBuffer[1], &swBuffer[0], maxIndex * sizeof(SuggestedWord*)); + memmove(&swBuffer[1], &swBuffer[0], maxIndex * sizeof(SuggestedWord *)); swBuffer[0] = nsMaxSw; } } @@ -140,12 +140,13 @@ class WordsPriorityQueue { continue; } const unsigned int wordLength = sw->mWordLength; - char *targetAdr = (char*) outputChars + i * MAX_WORD_LENGTH * sizeof(short); + char *targetAddress = reinterpret_cast(outputChars) + + i * MAX_WORD_LENGTH * sizeof(short); frequencies[i] = sw->mScore; outputTypes[i] = sw->mType; - memcpy(targetAdr, sw->mWord, (wordLength) * sizeof(short)); + memcpy(targetAddress, sw->mWord, (wordLength) * sizeof(short)); if (wordLength < MAX_WORD_LENGTH) { - ((unsigned short*) targetAdr)[wordLength] = 0; + reinterpret_cast(targetAddress)[wordLength] = 0; } sw->mUsed = false; } From 507113a1bb0d9b7ac333512a69da12c4d5d57182 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Mon, 13 Aug 2012 11:36:41 +0900 Subject: [PATCH 038/133] Fix typo in include guard macro, and some cleanups. Change-Id: I455f6a52667b1c6ae5eb9f58b3494dd87eb2c3f5 --- native/jni/src/geometry_utils.h | 46 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h index 65611b9ba..168542e45 100644 --- a/native/jni/src/geometry_utils.h +++ b/native/jni/src/geometry_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef LATINIME_INCREMENTAL_GEOMETRY_UTILS_H -#define LATINIME_INCREMENTAL_GEOMETRY_UTILS_H +#ifndef LATINIME_GEOMETRY_UTILS_H +#define LATINIME_GEOMETRY_UTILS_H #include @@ -49,21 +49,18 @@ static inline int getDistanceInt(int x1, int y1, int x2, int y2) { } static inline float getAngle(int x1, int y1, int x2, int y2) { - int dx = x1 - x2; - int dy = y1 - y2; + const int dx = x1 - x2; + const int dy = y1 - y2; if (dx == 0 && dy == 0) { return 0; } - float dxf = static_cast(dx); - float dyf = static_cast(dy); + const float dxf = static_cast(dx); + const float dyf = static_cast(dy); return atan2f(dyf, dxf); } static inline float angleDiff(float a1, float a2) { - float diff = a1 - a2; - if (diff < 0) { - diff = -diff; - } + const float diff = fabsf(a1 - a2); if (diff > M_PI_F) { return 2.0f * M_PI_F - diff; } @@ -76,32 +73,33 @@ static inline float angleDiff(float a1, float a2) { // float B = y - y1; // float C = x2 - x1; // float D = y2 - y1; -// return abs(A * D - C * B) / sqrt(C * C + D * D); +// return fabsf(A * D - C * B) / sqrtf(C * C + D * D); // } static inline float pointToLineSegDistanceSqrFloat( float x, float y, float x1, float y1, float x2, float y2) { - float ray1x = x - x1; - float ray1y = y - y1; - float ray2x = x2 - x1; - float ray2y = y2 - y1; + const float ray1x = x - x1; + const float ray1y = y - y1; + const float ray2x = x2 - x1; + const float ray2y = y2 - y1; - float dotProduct = ray1x * ray2x + ray1y * ray2y; - float lineLengthSq = ray2x * ray2x + ray2y * ray2y; - float projectionLengthSq = dotProduct / lineLengthSq; + const float dotProduct = ray1x * ray2x + ray1y * ray2y; + const float lineLengthSqr = sqrf(ray2x) + sqrf(ray2y); + const float projectionLengthSqr = dotProduct / lineLengthSqr; - float projectionX, projectionY; - if (projectionLengthSq < 0) { + float projectionX; + float projectionY; + if (projectionLengthSqr < 0.0f) { projectionX = x1; projectionY = y1; - } else if (projectionLengthSq > 1) { + } else if (projectionLengthSqr > 1.0f) { projectionX = x2; projectionY = y2; } else { - projectionX = x1 + projectionLengthSq * ray2x; - projectionY = y1 + projectionLengthSq * ray2y; + projectionX = x1 + projectionLengthSqr * ray2x; + projectionY = y1 + projectionLengthSqr * ray2y; } return getDistanceSqrFloat(x, y, projectionX, projectionY); } } // namespace latinime -#endif // LATINIME_INCREMENTAL_GEOMETRY_UTILS_H +#endif // LATINIME_GEOMETRY_UTILS_H From 708cc94a354ad96294126a6ba954bf8be5bd5a06 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 13 Aug 2012 11:54:31 +0900 Subject: [PATCH 039/133] Remove gesture detection hacking code Change-Id: Idaec3753592ca0a5c5545eb5ab65254a3e32e662 --- .../inputmethod/keyboard/PointerTracker.java | 9 +-------- .../keyboard/internal/GestureStroke.java | 17 +++-------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 85360c410..faa08db24 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -29,7 +29,6 @@ import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; @@ -130,10 +129,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static final InputPointers sAggregratedPointers = new InputPointers( GestureStroke.DEFAULT_CAPACITY); private static PointerTrackerQueue sPointerTrackerQueue; - // HACK: Change gesture detection criteria depending on this variable. - // TODO: Find more comprehensive ways to detect a gesture start. - // True when the previous user input was a gesture input, not a typing input. - private static boolean sWasInGesture; public final int mPointerId; @@ -586,7 +581,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { mListener.onEndBatchInput(batchPoints); clearBatchInputRecognitionStateOfThisPointerTracker(); clearBatchInputPointsOfAllPointerTrackers(); - sWasInGesture = true; } private void abortBatchInput() { @@ -719,7 +713,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (sShouldHandleGesture && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; stroke.addPoint(x, y, gestureTime, isHistorical); - if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) { + if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) { startBatchInput(); } } @@ -1002,7 +996,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { int code = key.mCode; callListenerOnCodeInput(key, code, x, y); callListenerOnRelease(key, code, false); - sWasInGesture = false; } private void printTouchEvent(String title, int x, int y, long eventTime) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 5ccb40f5d..778e8e5ef 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -37,14 +37,11 @@ public class GestureStroke { private int mLastPointY; private int mMinGestureLength; - private int mMinGestureLengthWhileInGesture; private int mMinGestureSampleLength; // TODO: Move some of these to resource. - private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f; - private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE = 0.5f; - private static final int MIN_GESTURE_DURATION = 150; // msec - private static final int MIN_GESTURE_DURATION_WHILE_IN_GESTURE = 75; // msec + private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 0.75f; + private static final int MIN_GESTURE_DURATION = 100; // msec private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f; private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f); @@ -63,18 +60,10 @@ public class GestureStroke { public void setGestureSampleLength(final int keyWidth, final int keyHeight) { // 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); - mMinGestureLengthWhileInGesture = (int)( - keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE); mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT); } - public boolean isStartOfAGesture(final int downDuration, final boolean wasInGesture) { - // The tolerance of the time duration and the stroke length to detect the start of a - // gesture stroke should be eased when the previous input was a gesture input. - if (wasInGesture) { - return downDuration > MIN_GESTURE_DURATION_WHILE_IN_GESTURE - && mLength > mMinGestureLengthWhileInGesture; - } + public boolean isStartOfAGesture(final int downDuration) { return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength; } From fc5e3f973c043e88e1859f5dafe8df9b417dc3ca Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 13 Aug 2012 12:44:10 +0900 Subject: [PATCH 040/133] Remove unnecessary drawing point Change-Id: Ib9cba3484a72306320eb9c1744d940da1a0998c7 --- .../com/android/inputmethod/keyboard/PointerTracker.java | 2 +- .../inputmethod/keyboard/internal/GestureStroke.java | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index faa08db24..99b43bb3a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -521,7 +521,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { public void drawGestureTrail(Canvas canvas, Paint paint) { if (mInGesture) { - mGestureStroke.drawGestureTrail(canvas, paint, mLastX, mLastY); + mGestureStroke.drawGestureTrail(canvas, paint); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 778e8e5ef..fc813a3d9 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -166,12 +166,12 @@ public class GestureStroke { return diff; } - public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) { + public void drawGestureTrail(final Canvas canvas, final Paint paint) { // TODO: These paint parameter interpolation should be tunable, possibly introduce an object // that implements an interface such as Paint getPaint(int step, int strokePoints) final int size = mXCoordinates.getLength(); - int[] xCoords = mXCoordinates.getPrimitiveArray(); - int[] yCoords = mYCoordinates.getPrimitiveArray(); + final int[] xCoords = mXCoordinates.getPrimitiveArray(); + final int[] yCoords = mYCoordinates.getPrimitiveArray(); int alpha = Constants.Color.ALPHA_OPAQUE; for (int i = size - 1; i > 0 && alpha > 0; i--) { paint.setAlpha(alpha); @@ -179,9 +179,6 @@ public class GestureStroke { alpha -= DRAWING_GESTURE_FADE_RATE; } canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint); - if (i == size - 1) { - canvas.drawLine(lastX, lastY, xCoords[i], yCoords[i], paint); - } } } } From ddb12ea63ab75c6420d262a68b709d0fce1be6d6 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Mon, 13 Aug 2012 14:32:17 +0900 Subject: [PATCH 041/133] Step 24-A remove setdict Change-Id: Iab28bf0fd785b9e81fc86837e64a1ef2e315a229 --- native/jni/src/dictionary.cpp | 2 -- native/jni/src/gesture/gesture_decoder_wrapper.h | 8 -------- native/jni/src/gesture/incremental_decoder_interface.h | 2 -- native/jni/src/gesture/incremental_decoder_wrapper.h | 8 -------- 4 files changed, 20 deletions(-) diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index 75d566cc7..9e4bd15a9 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -47,8 +47,6 @@ Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, fullWordMultiplier, maxWordLength, maxWords, options); mBigramDictionary = new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions); mGestureDecoder = new GestureDecoderWrapper(maxWordLength, maxWords); - mGestureDecoder->setDict(mUnigramDictionary, mBigramDictionary, - mOffsetDict /* dict root */, 0 /* root pos */); } Dictionary::~Dictionary() { diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index 4a26d9c1b..f8bfe7c79 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -48,14 +48,6 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } - void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, - const uint8_t *dictRoot, int rootPos) { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->setDict(dict, bigram, dictRoot, rootPos); - } - static void setGestureDecoderFactoryMethod( IncrementalDecoderInterface *(*factoryMethod)(int, int)) { sGestureDecoderFactoryMethod = factoryMethod; diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index e940344d4..04f0095e0 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -32,8 +32,6 @@ class IncrementalDecoderInterface { int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) = 0; - virtual void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, - const uint8_t *dictRoot, int rootPos) = 0; IncrementalDecoderInterface() { }; virtual ~IncrementalDecoderInterface() { }; private: diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index f47d539b7..5cb2ee368 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -48,14 +48,6 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { inputSize, commitPoint, outWords, frequencies, outputIndices, outputTypes); } - void setDict(const UnigramDictionary *dict, const BigramDictionary *bigram, - const uint8_t *dictRoot, int rootPos) { - if (!mIncrementalDecoderInterface) { - return; - } - mIncrementalDecoderInterface->setDict(dict, bigram, dictRoot, rootPos); - } - static void setIncrementalDecoderFactoryMethod( IncrementalDecoderInterface *(*factoryMethod)(int, int)) { sIncrementalDecoderFactoryMethod = factoryMethod; From c2781427459866572ab09cf5824ed6335a3222ad Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 13 Aug 2012 15:53:07 +0900 Subject: [PATCH 042/133] Remove useless backslashes from the whitelist dictionary For some reason, these are necessary for resources, but XML standard does not require them. Change-Id: I7cdaecb6815aa4020e0d453e33be38ff2968df50 --- dictionaries/en_whitelist.xml | 82 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/dictionaries/en_whitelist.xml b/dictionaries/en_whitelist.xml index f112077bd..e11935fdf 100644 --- a/dictionaries/en_whitelist.xml +++ b/dictionaries/en_whitelist.xml @@ -19,13 +19,13 @@ --> - + - + @@ -34,7 +34,7 @@ - + @@ -49,13 +49,13 @@ - + - + @@ -82,13 +82,13 @@ - + - + - + @@ -97,19 +97,19 @@ - + - + - + - + - + @@ -121,25 +121,25 @@ - + - + - + - + - + - + @@ -196,10 +196,10 @@ - + - + @@ -208,7 +208,7 @@ - + @@ -238,60 +238,60 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + From b14fc88e482e53ba6852c8d5da5d9826c68d041f Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 10 Aug 2012 16:52:27 +0900 Subject: [PATCH 043/133] Tag the whitelisted entries in native code. Since this is already used in Java land, this actually does activate the whitelist path, and the code is now fully functional. We still have to remove the old whitelist resource and to compile the dictionary that includes the whitelist. Bug: 6906525 Change-Id: Iacde5313e303b9ed792940efaf6bcfa4ee1317bd --- native/jni/src/binary_format.h | 7 +++++++ native/jni/src/correction.cpp | 4 ++++ native/jni/src/correction.h | 1 + native/jni/src/proximity_info_state.h | 30 +++++++++++++-------------- native/jni/src/terminal_attributes.h | 3 ++- native/jni/src/unigram_dictionary.cpp | 25 +++++++++++++++++----- 6 files changed, 49 insertions(+), 21 deletions(-) diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index 2ee4077c1..4cabc8404 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -52,6 +52,8 @@ class BinaryFormat { // Mask for attribute frequency, stored on 4 bits inside the flags byte. static const int MASK_ATTRIBUTE_FREQUENCY = 0x0F; + // The numeric value of the shortcut frequency that means 'whitelist'. + static const int WHITELIST_SHORTCUT_FREQUENCY = 15; // Mask and flags for attribute address type selection. static const int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30; @@ -99,6 +101,7 @@ class BinaryFormat { static bool hasChildrenInFlags(const uint8_t flags); static int getAttributeAddressAndForwardPointer(const uint8_t *const dict, const uint8_t flags, int *pos); + static int getAttributeFrequencyFromFlags(const int flags); static int getTerminalPosition(const uint8_t *const root, const int32_t *const inWord, const int length, const bool forceLowerCaseSearch); static int getWordAtAddress(const uint8_t *const root, const int address, const int maxDepth, @@ -340,6 +343,10 @@ inline int BinaryFormat::getAttributeAddressAndForwardPointer(const uint8_t *con } } +inline int BinaryFormat::getAttributeFrequencyFromFlags(const int flags) { + return flags & MASK_ATTRIBUTE_FREQUENCY; +} + // This function gets the byte position of the last chargroup of the exact matching word in the // dictionary. If no match is found, it returns NOT_VALID_WORD. inline int BinaryFormat::getTerminalPosition(const uint8_t *const root, diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index b18b35e8c..c815dabe6 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -157,6 +157,10 @@ void Correction::checkState() { } } +bool Correction::sameAsTyped() { + return mProximityInfoState.sameAsTyped(mWord, mOutputIndex); +} + int Correction::getFreqForSplitMultipleWords(const int *freqArray, const int *wordLengthArray, const int wordCount, const bool isSpaceProximity, const unsigned short *word) { return Correction::RankingAlgorithm::calcFreqForSplitMultipleWords(freqArray, wordLengthArray, diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h index 81623a46b..57e7b7189 100644 --- a/native/jni/src/correction.h +++ b/native/jni/src/correction.h @@ -105,6 +105,7 @@ class Correction { const int spaceProximityPos, const int missingSpacePos, const bool useFullEditDistance, const bool doAutoCompletion, const int maxErrors); void checkState(); + bool sameAsTyped(); bool initProcessState(const int index); int getInputIndex(); diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 76d45516e..474c40757 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -160,6 +160,21 @@ class ProximityInfoState { return mTouchPositionCorrectionEnabled; } + inline bool sameAsTyped(const unsigned short *word, int length) const { + if (length != mInputLength) { + return false; + } + const int *inputCodes = mInputCodes; + while (length--) { + if (static_cast(*inputCodes) != static_cast(*word)) { + return false; + } + inputCodes += MAX_PROXIMITY_CHARS_SIZE_INTERNAL; + word++; + } + return true; + } + private: DISALLOW_COPY_AND_ASSIGN(ProximityInfoState); ///////////////////////////////////////// @@ -179,21 +194,6 @@ class ProximityInfoState { return mInputXCoordinates && mInputYCoordinates; } - bool sameAsTyped(const unsigned short *word, int length) const { - if (length != mInputLength) { - return false; - } - const int *inputCodes = mInputCodes; - while (length--) { - if ((unsigned int) *inputCodes != (unsigned int) *word) { - return false; - } - inputCodes += MAX_PROXIMITY_CHARS_SIZE_INTERNAL; - word++; - } - return true; - } - // const const ProximityInfo *mProximityInfo; bool mHasTouchPositionCorrectionData; diff --git a/native/jni/src/terminal_attributes.h b/native/jni/src/terminal_attributes.h index d63364514..1ae9c7cbb 100644 --- a/native/jni/src/terminal_attributes.h +++ b/native/jni/src/terminal_attributes.h @@ -46,7 +46,7 @@ class TerminalAttributes { // Gets the shortcut target itself as a uint16_t string. For parameters and return value // see BinaryFormat::getWordAtAddress. // TODO: make the output an uint32_t* to handle the whole unicode range. - inline int getNextShortcutTarget(const int maxDepth, uint16_t *outWord) { + inline int getNextShortcutTarget(const int maxDepth, uint16_t *outWord, int *outFreq) { const int shortcutFlags = BinaryFormat::getFlagsAndForwardPointer(mDict, &mPos); mHasNextShortcutTarget = 0 != (shortcutFlags & BinaryFormat::FLAG_ATTRIBUTE_HAS_NEXT); @@ -56,6 +56,7 @@ class TerminalAttributes { if (NOT_A_CHARACTER == charCode) break; outWord[i] = (uint16_t)charCode; } + *outFreq = BinaryFormat::getAttributeFrequencyFromFlags(shortcutFlags); mPos += BinaryFormat::CHARACTER_ARRAY_TERMINATOR_SIZE; return i; } diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index 9f7ab5362..cc6d39a29 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -391,8 +391,12 @@ inline void UnigramDictionary::onTerminal(const int probability, const int finalProbability = correction->getFinalProbability(probability, &wordPointer, &wordLength); if (finalProbability != NOT_A_PROBABILITY) { - addWord(wordPointer, wordLength, finalProbability, masterQueue, - Dictionary::KIND_CORRECTION); + if (0 != finalProbability) { + // If the probability is 0, we don't want to add this word. However we still + // want to add its shortcuts (including a possible whitelist entry) if any. + addWord(wordPointer, wordLength, finalProbability, masterQueue, + Dictionary::KIND_CORRECTION); + } const int shortcutProbability = finalProbability > 0 ? finalProbability - 1 : 0; // Please note that the shortcut candidates will be added to the master queue only. @@ -407,10 +411,21 @@ inline void UnigramDictionary::onTerminal(const int probability, // with the same score. For the moment we use -1 to make sure the shortcut will // never be in front of the word. uint16_t shortcutTarget[MAX_WORD_LENGTH_INTERNAL]; + int shortcutFrequency; const int shortcutTargetStringLength = iterator.getNextShortcutTarget( - MAX_WORD_LENGTH_INTERNAL, shortcutTarget); - addWord(shortcutTarget, shortcutTargetStringLength, shortcutProbability, - masterQueue, Dictionary::KIND_CORRECTION); + MAX_WORD_LENGTH_INTERNAL, shortcutTarget, &shortcutFrequency); + int shortcutScore; + int kind; + if (shortcutFrequency == BinaryFormat::WHITELIST_SHORTCUT_FREQUENCY + && correction->sameAsTyped()) { + shortcutScore = S_INT_MAX; + kind = Dictionary::KIND_WHITELIST; + } else { + shortcutScore = shortcutProbability; + kind = Dictionary::KIND_CORRECTION; + } + addWord(shortcutTarget, shortcutTargetStringLength, shortcutScore, + masterQueue, kind); } } } From 0fb950858e4dae7ea1602c27e741051f9421f9cb Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Mon, 13 Aug 2012 17:31:46 +0900 Subject: [PATCH 044/133] Cleanups in geometry_utils.h Change-Id: Ic051703e5b67adfc62753ebd9ab254296719c6e9 --- .../keyboard/internal/GestureStroke.java | 17 ++++---- native/jni/src/geometry_utils.h | 40 +++++++++---------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 778e8e5ef..0fec64582 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -142,20 +142,21 @@ public class GestureStroke { mLastIncrementalBatchSize = size; } - private static float getDistance(final int p1x, final int p1y, - final int p2x, final int p2y) { - final float dx = p1x - p2x; - final float dy = p1y - p2y; + 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; // 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); } - private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) { - final int dx = p1x - p2x; - final int dy = p1y - p2y; + private static float getAngle(final int x1, final int y1, final int x2, final int y2) { + final int dx = x1 - x2; + final int dy = y1 - y2; if (dx == 0 && dy == 0) return 0; - return (float)Math.atan2(dy, dx); + // Would it be faster to call atan2f() directly via JNI? Not sure about what the JIT + // does with Math.atan2(). + return (float)Math.atan2((double)dy, (double)dx); } private static float getAngleDiff(final float a1, final float a2) { diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h index 168542e45..deb042525 100644 --- a/native/jni/src/geometry_utils.h +++ b/native/jni/src/geometry_utils.h @@ -28,38 +28,36 @@ namespace latinime { -static inline float sqrf(float x) { +static inline float squareFloat(float x) { return x * x; } -static inline float getNormalizedSqrDistanceFloat(int x1, int y1, int x2, int y2, int scale) { - return sqrf(static_cast(x1 - x2) / static_cast(scale)) - + sqrf(static_cast(y1 - y2) / static_cast(scale)); +static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2, + float scale) { + return squareFloat((x1 - x2) / scale) + squareFloat((y1 - y2) / scale); } -static inline float getDistanceSqrFloat(float x1, float y1, float x2, float y2) { - return sqrf(x2 - x1) + sqrf(y2 - y1); +static inline float getSquaredDistanceFloat(float x1, float y1, float x2, float y2) { + return squareFloat(x1 - x2) + squareFloat(y1 - y2); +} + +static inline float getDistanceFloat(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); } static inline int getDistanceInt(int x1, int y1, int x2, int y2) { - return static_cast( - sqrtf(getDistanceSqrFloat( - static_cast(x1), static_cast(y1), - static_cast(x2), static_cast(y2)))); + return static_cast(getDistanceFloat(static_cast(x1), static_cast(y1), + static_cast(x2), static_cast(y2))); } static inline float getAngle(int x1, int y1, int x2, int y2) { const int dx = x1 - x2; const int dy = y1 - y2; - if (dx == 0 && dy == 0) { - return 0; - } - const float dxf = static_cast(dx); - const float dyf = static_cast(dy); - return atan2f(dyf, dxf); + if (dx == 0 && dy == 0) return 0; + return atan2f(static_cast(dy), static_cast(dx)); } -static inline float angleDiff(float a1, float a2) { +static inline float getAngleDiff(float a1, float a2) { const float diff = fabsf(a1 - a2); if (diff > M_PI_F) { return 2.0f * M_PI_F - diff; @@ -67,7 +65,7 @@ static inline float angleDiff(float a1, float a2) { return diff; } -// static float pointToLineDistanceSqrFloat( +// static float pointToLineSegSquaredDistanceFloat( // float x, float y, float x1, float y1, float x2, float y2) { // float A = x - x1; // float B = y - y1; @@ -76,7 +74,7 @@ static inline float angleDiff(float a1, float a2) { // return fabsf(A * D - C * B) / sqrtf(C * C + D * D); // } -static inline float pointToLineSegDistanceSqrFloat( +static inline float pointToLineSegSquaredDistanceFloat( float x, float y, float x1, float y1, float x2, float y2) { const float ray1x = x - x1; const float ray1y = y - y1; @@ -84,7 +82,7 @@ static inline float pointToLineSegDistanceSqrFloat( const float ray2y = y2 - y1; const float dotProduct = ray1x * ray2x + ray1y * ray2y; - const float lineLengthSqr = sqrf(ray2x) + sqrf(ray2y); + const float lineLengthSqr = squareFloat(ray2x) + squareFloat(ray2y); const float projectionLengthSqr = dotProduct / lineLengthSqr; float projectionX; @@ -99,7 +97,7 @@ static inline float pointToLineSegDistanceSqrFloat( projectionX = x1 + projectionLengthSqr * ray2x; projectionY = y1 + projectionLengthSqr * ray2y; } - return getDistanceSqrFloat(x, y, projectionX, projectionY); + return getSquaredDistanceFloat(x, y, projectionX, projectionY); } } // namespace latinime #endif // LATINIME_GEOMETRY_UTILS_H From 74d0bb09c700aec91afd120688c56498d93e3110 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 13 Aug 2012 13:45:32 +0900 Subject: [PATCH 045/133] Eliminate redundant time parameter from gesture detection code This change also uses the key width to determine the sampling rate of the gesture points (instead of the key height). Change-Id: I987487825693b4883d8359eb5b31f1d668cd5ef4 --- .../android/inputmethod/keyboard/PointerTracker.java | 5 ++--- .../inputmethod/keyboard/internal/GestureStroke.java | 10 ++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index faa08db24..696903746 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -408,8 +408,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard(); - mGestureStroke.setGestureSampleLength( - mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight); + mGestureStroke.setGestureSampleLength(mKeyboard.mMostCommonKeyWidth); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); if (newKey != mCurrentKey) { if (mDrawingProxy != null) { @@ -713,7 +712,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { if (sShouldHandleGesture && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; stroke.addPoint(x, y, gestureTime, isHistorical); - if (!mInGesture && stroke.isStartOfAGesture(gestureTime)) { + if (!mInGesture && stroke.isStartOfAGesture()) { startBatchInput(); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 778e8e5ef..da57b5a34 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -42,7 +42,7 @@ public class GestureStroke { // 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_DURATION = 100; // msec - private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 1.0f / 6.0f; + private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f; private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f); @@ -57,13 +57,15 @@ public class GestureStroke { reset(); } - public void setGestureSampleLength(final int keyWidth, final int keyHeight) { + public void setGestureSampleLength(final int 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)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT); + mMinGestureSampleLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH); } - public boolean isStartOfAGesture(final int downDuration) { + public boolean isStartOfAGesture() { + final int size = mEventTimes.getLength(); + final int downDuration = (size > 0) ? mEventTimes.get(size - 1) : 0; return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength; } From e7398cdb2b48eb52dc9676c8efa75bc7cb9af3e9 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Mon, 13 Aug 2012 20:20:04 +0900 Subject: [PATCH 046/133] Step 26-A Cleanup geometory_utils Change-Id: I839c498c85009a5d6428a71f121a2906fd667db8 --- native/jni/src/geometry_utils.h | 5 ----- native/jni/src/proximity_info.cpp | 37 +++++++++++++------------------ native/jni/src/proximity_info.h | 2 ++ 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h index deb042525..146eb8055 100644 --- a/native/jni/src/geometry_utils.h +++ b/native/jni/src/geometry_utils.h @@ -32,11 +32,6 @@ static inline float squareFloat(float x) { return x * x; } -static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2, - float scale) { - return squareFloat((x1 - x2) / scale) + squareFloat((y1 - y2) / scale); -} - static inline float getSquaredDistanceFloat(float x1, float y1, float x2, float y2) { return squareFloat(x1 - x2) + squareFloat(y1 - y2); } diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index ade78a1ec..1b9bac0f0 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -135,6 +135,21 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { return false; } +static inline float getNormalizedSquaredDistanceFloat(float x1, float y1, float x2, float y2, + float scale) { + return squareFloat((x1 - x2) / scale) + squareFloat((y1 - y2) / scale); +} + +float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloat( + const int keyId, const int x, const int y) const { + const float centerX = static_cast(getKeyCenterXOfIdG(keyId)); + const float centerY = static_cast(getKeyCenterYOfIdG(keyId)); + const float touchX = static_cast(x); + const float touchY = static_cast(y); + const float keyWidth = static_cast(getMostCommonKeyWidth()); + return getNormalizedSquaredDistanceFloat(centerX, centerY, touchX, touchY, keyWidth); +} + int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) const { if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case const int left = mKeyXCoordinates[keyId]; @@ -233,6 +248,7 @@ int ProximityInfo::getKeyCode(const int keyIndex) const { } void ProximityInfo::initializeG() { + // TODO: Optimize for (int i = 0; i < KEY_COUNT; ++i) { const int code = mKeyCharCodes[i]; const int lowerCode = toBaseLowerCase(code); @@ -285,25 +301,4 @@ int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const { } return 0; } - -// TODO: [Staging] Optimize -void ProximityInfo::getCenters(int *centerXs, int *centerYs, int *codeToKeyIndex, - int *keyToCodeIndex, int *keyCount, int *keyWidth) const { - *keyCount = KEY_COUNT; - *keyWidth = sqrtf(static_cast(MOST_COMMON_KEY_WIDTH_SQUARE)); - - for (int i = 0; i < KEY_COUNT; ++i) { - const int code = mKeyCharCodes[i]; - const int lowerCode = toBaseLowerCase(code); - centerXs[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; - centerYs[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; - codeToKeyIndex[code] = i; - if (code != lowerCode && lowerCode >= 0 && lowerCode <= MAX_CHAR_CODE) { - codeToKeyIndex[lowerCode] = i; - keyToCodeIndex[i] = lowerCode; - } else { - keyToCodeIndex[i] = code; - } - } -} } // namespace latinime diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 58f2d7502..8a407e71a 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -38,6 +38,8 @@ class ProximityInfo { ~ProximityInfo(); bool hasSpaceProximity(const int x, const int y) const; int getNormalizedSquaredDistance(const int inputIndex, const int proximityIndex) const; + float getNormalizedSquaredDistanceFromCenterFloat( + const int keyId, const int x, const int y) const; bool sameAsTyped(const unsigned short *word, int length) const; int squaredDistanceToEdge(const int keyId, const int x, const int y) const; bool isOnKey(const int keyId, const int x, const int y) const { From 0aeb4c206c9d9d30d9b49dcc4a742130ba4f1da7 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Mon, 13 Aug 2012 14:05:29 -0700 Subject: [PATCH 047/133] Import translations. DO NOT MERGE Change-Id: I36bb28cfdf548a38bfdd3127ddafa6c7d938376e Auto-generated-cl: translation import --- java/res/values-in/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index 8273d5483..922991053 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -140,7 +140,7 @@ "Aktifkan" "Nanti saja" "Sudah ada gaya masukan yang sama: %s" - "Modus studi daya guna" + "Mode studi daya guna" "Setelan durasi getaran saat tombol ditekan" "Setelan volume suara saat tombol ditekan" From 5d6b8e181811e10b38dd30f4ba33757bb2162552 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 14 Aug 2012 14:45:17 +0900 Subject: [PATCH 048/133] Fix a bug that would prevent spell checking java.lang.ArrayIndexOutOfBoundsException: [I offset=0 length=528 src.length=512 com.android.inputmethod.keyboard.ProximityInfo .setProximityInfoNative(Native Method) com.android.inputmethod.keyboard.ProximityInfo .createSpellCheckerProximityInfo(ProximityInfo.java:85) com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService .createDictAndProximity(AndroidSpellCheckerService.java:411) The array was too small with respect with the passed size. Change-Id: I7777d7aecd17375d3fbf82854a66eb7a5a289ce6 --- .../inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index 0103e8423..bd92d883b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -111,6 +111,7 @@ public class SpellCheckerProximityInfo { NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, + NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, }; static { buildProximityIndices(PROXIMITY, INDICES); From e30c05800fa463ef622132b0df466f5455281fc1 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 14 Aug 2012 15:11:53 +0900 Subject: [PATCH 049/133] Preemptive anti-deadlock device We don't know of any actual bug in the implementation of the pool that may result in a deadlock, but this still implements a means out of a deadlock if one ever arises anyway. It's meant to improve the resilience of the spell checker. Bug: 5441027 Change-Id: I689491090792dbb89110ee56631965c5bb2dc712 --- .../AndroidWordLevelSpellCheckerSession.java | 4 +-- .../latin/spellcheck/DictionaryPool.java | 30 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 0171dc06d..06f5db749 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -193,7 +193,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { if (shouldFilterOut(inText, mScript)) { DictAndProximity dictInfo = null; try { - dictInfo = mDictionaryPool.takeOrGetNull(); + dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (null == dictInfo) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } @@ -236,7 +236,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { boolean isInDict = true; DictAndProximity dictInfo = null; try { - dictInfo = mDictionaryPool.takeOrGetNull(); + dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (null == dictInfo) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 8fc632ee7..83f82faeb 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -16,14 +16,24 @@ package com.android.inputmethod.latin.spellcheck; +import android.util.Log; + import java.util.Locale; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * A blocking queue that creates dictionaries up to a certain limit as necessary. + * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we + * will clear the queue and generate its contents again. This is transparent for + * the client code, but may help with sloppy clients. */ @SuppressWarnings("serial") public class DictionaryPool extends LinkedBlockingQueue { + private final static String TAG = DictionaryPool.class.getSimpleName(); + // How many seconds we wait for a dictionary to become available. Past this delay, we give up in + // fear some bug caused a deadlock, and reset the whole pool. + private final static int TIMEOUT = 3; private final AndroidSpellCheckerService mService; private final int mMaxSize; private final Locale mLocale; @@ -41,13 +51,23 @@ public class DictionaryPool extends LinkedBlockingQueue { } @Override - public DictAndProximity take() throws InterruptedException { + public DictAndProximity poll(final long timeout, final TimeUnit unit) + throws InterruptedException { final DictAndProximity dict = poll(); if (null != dict) return dict; synchronized(this) { if (mSize >= mMaxSize) { - // Our pool is already full. Wait until some dictionary is ready. - return super.take(); + // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT + // expires to avoid a deadlock. + final DictAndProximity result = super.poll(timeout, unit); + if (null == result) { + Log.e(TAG, "Deadlock detected ! Resetting dictionary pool"); + clear(); + mSize = 1; + return mService.createDictAndProximity(mLocale); + } else { + return result; + } } else { ++mSize; return mService.createDictAndProximity(mLocale); @@ -56,9 +76,9 @@ public class DictionaryPool extends LinkedBlockingQueue { } // Convenience method - public DictAndProximity takeOrGetNull() { + public DictAndProximity pollWithDefaultTimeout() { try { - return take(); + return poll(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { return null; } From 34710b03e6d890a61c8c570ff9e769493f6955f9 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Tue, 14 Aug 2012 14:22:27 +0900 Subject: [PATCH 050/133] A couple of cleanups. Remove unnecessary casts. Change-Id: Ice530aa83a9a688da35aec408be2a60242699157 --- ...oid_inputmethod_keyboard_ProximityInfo.cpp | 2 +- ...oid_inputmethod_latin_BinaryDictionary.cpp | 33 +++++++++---------- ...d_inputmethod_latin_DicTraverseSession.cpp | 6 ++-- native/jni/jni_common.cpp | 2 +- native/jni/src/dictionary.h | 12 +++---- native/jni/src/words_priority_queue.h | 2 +- 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index 545d91a70..560b3a533 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -37,7 +37,7 @@ static jlong latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, } static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximityInfo) { - ProximityInfo *pi = reinterpret_cast(proximityInfo); + ProximityInfo *pi = reinterpret_cast(proximityInfo); delete pi; } diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 5a287a122..cf55580be 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -27,6 +27,7 @@ #include #else // USE_MMAP_FOR_DICTIONARY #include +#include // for fopen() etc. #endif // USE_MMAP_FOR_DICTIONARY #include "binary_format.h" @@ -40,7 +41,7 @@ namespace latinime { class ProximityInfo; -static void releaseDictBuf(void *dictBuf, const size_t length, int fd); +static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd); static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, jstring sourceDir, jlong dictOffset, jlong dictSize, @@ -75,7 +76,7 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); return 0; } - dictBuf = reinterpret_cast(reinterpret_cast(dictBuf) + adjust); + dictBuf = static_cast(dictBuf) + adjust; #else // USE_MMAP_FOR_DICTIONARY /* malloc version */ FILE *file = 0; @@ -111,10 +112,10 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object, } Dictionary *dictionary = 0; if (BinaryFormat::UNKNOWN_FORMAT - == BinaryFormat::detectFormat(reinterpret_cast(dictBuf))) { + == BinaryFormat::detectFormat(static_cast(dictBuf))) { AKLOGE("DICT: dictionary format is unknown, bad magic number"); #ifdef USE_MMAP_FOR_DICTIONARY - releaseDictBuf(reinterpret_cast(dictBuf) - adjust, adjDictSize, fd); + releaseDictBuf(static_cast(dictBuf) - adjust, adjDictSize, fd); #else // USE_MMAP_FOR_DICTIONARY releaseDictBuf(dictBuf, 0, 0); #endif // USE_MMAP_FOR_DICTIONARY @@ -134,10 +135,10 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jintArray prevWordCodePointsForBigrams, jboolean useFullEditDistance, jcharArray outputCharsArray, jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray) { - Dictionary *dictionary = reinterpret_cast(dict); + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return 0; - ProximityInfo *pInfo = reinterpret_cast(proximityInfo); - void *traverseSession = reinterpret_cast(dicTraverseSession); + ProximityInfo *pInfo = reinterpret_cast(proximityInfo); + void *traverseSession = reinterpret_cast(dicTraverseSession); // Input values int xCoordinates[arraySize]; @@ -199,7 +200,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, static jint latinime_BinaryDictionary_getFrequency(JNIEnv *env, jobject object, jlong dict, jintArray wordArray) { - Dictionary *dictionary = reinterpret_cast(dict); + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return 0; const jsize codePointLength = env->GetArrayLength(wordArray); int codePoints[codePointLength]; @@ -209,7 +210,7 @@ static jint latinime_BinaryDictionary_getFrequency(JNIEnv *env, jobject object, static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jobject object, jlong dict, jintArray wordArray1, jintArray wordArray2) { - Dictionary *dictionary = reinterpret_cast(dict); + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return (jboolean) false; const jsize codePointLength1 = env->GetArrayLength(wordArray1); const jsize codePointLength2 = env->GetArrayLength(wordArray2); @@ -247,14 +248,12 @@ static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, } static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) { - Dictionary *dictionary = reinterpret_cast(dict); + Dictionary *dictionary = reinterpret_cast(dict); if (!dictionary) return; - void *dictBuf = dictionary->getDict(); + const void *dictBuf = dictionary->getDict(); if (!dictBuf) return; #ifdef USE_MMAP_FOR_DICTIONARY - releaseDictBuf( - reinterpret_cast( - reinterpret_cast(dictBuf) - dictionary->getDictBufAdjust()), + releaseDictBuf(static_cast(dictBuf) - dictionary->getDictBufAdjust(), dictionary->getDictSize() + dictionary->getDictBufAdjust(), dictionary->getMmapFd()); #else // USE_MMAP_FOR_DICTIONARY releaseDictBuf(dictBuf, 0, 0); @@ -262,9 +261,9 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong d delete dictionary; } -static void releaseDictBuf(void *dictBuf, const size_t length, int fd) { +static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd) { #ifdef USE_MMAP_FOR_DICTIONARY - int ret = munmap(dictBuf, length); + int ret = munmap(const_cast(dictBuf), length); if (ret != 0) { AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno); } @@ -273,7 +272,7 @@ static void releaseDictBuf(void *dictBuf, const size_t length, int fd) { AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno); } #else // USE_MMAP_FOR_DICTIONARY - free(dictBuf); + free(const_cast(dictBuf)); #endif // USE_MMAP_FOR_DICTIONARY } diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 947360e8e..5d405f117 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -30,8 +30,8 @@ static jlong latinime_setDicTraverseSession(JNIEnv *env, jobject object, jstring static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession, jlong dictionary, jintArray previousWord, jint previousWordLength) { - void *ts = reinterpret_cast(traverseSession); - Dictionary *dict = reinterpret_cast(dictionary); + void *ts = reinterpret_cast(traverseSession); + Dictionary *dict = reinterpret_cast(dictionary); if (!previousWord) { DicTraverseWrapper::initDicTraverseSession(ts, dict, 0, 0); return; @@ -42,7 +42,7 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jobject object, jlong t } static void latinime_releaseDicTraverseSession(JNIEnv *env, jobject object, jlong traverseSession) { - void *ts = reinterpret_cast(traverseSession); + void *ts = reinterpret_cast(traverseSession); DicTraverseWrapper::releaseDicTraverseSession(ts); } diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp index 1586f253b..0da166903 100644 --- a/native/jni/jni_common.cpp +++ b/native/jni/jni_common.cpp @@ -34,7 +34,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = 0; jint result = -1; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { AKLOGE("ERROR: GetEnv failed"); goto bail; } diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h index 3b55e5de9..fd9e77011 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/dictionary.h @@ -55,11 +55,11 @@ class Dictionary { int getFrequency(const int32_t *word, int length) const; bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const; - void *getDict() const { // required to release dictionary buffer - return reinterpret_cast(const_cast(mDict)); + const uint8_t *getDict() const { // required to release dictionary buffer + return mDict; } - void *getOffsetDict() const { - return reinterpret_cast(const_cast(mOffsetDict)); + const uint8_t *getOffsetDict() const { + return mOffsetDict; } int getDictSize() const { return mDictSize; } int getMmapFd() const { return mMmapFd; } @@ -72,8 +72,8 @@ class Dictionary { private: DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary); - const unsigned char *mDict; - const unsigned char *mOffsetDict; + const uint8_t *mDict; + const uint8_t *mOffsetDict; // Used only for the mmap version of dictionary loading, but we use these as dummy variables // also for the malloc version. diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h index e97e16a12..2fad9bb3d 100644 --- a/native/jni/src/words_priority_queue.h +++ b/native/jni/src/words_priority_queue.h @@ -223,7 +223,7 @@ class WordsPriorityQueue { before, beforeLength, word, wordLength, score); } - typedef std::priority_queue, + typedef std::priority_queue, wordComparator> Suggestions; Suggestions mSuggestions; const unsigned int MAX_WORDS; From 267030dd82a6165ca56899d2988b5dc51ebea104 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Tue, 14 Aug 2012 18:54:13 +0900 Subject: [PATCH 051/133] Cleanup casts. Change-Id: I3bf33ca407cc3bee9f5c4c6f929cdb1421b92c50 --- ...m_android_inputmethod_latin_BinaryDictionary.cpp | 8 ++++---- native/jni/src/bigram_dictionary.cpp | 13 ++++++------- native/jni/src/char_utils.cpp | 7 +++---- native/jni/src/debug.h | 2 +- native/jni/src/dictionary.cpp | 4 ++-- native/jni/src/words_priority_queue.h | 7 +++---- 6 files changed, 19 insertions(+), 22 deletions(-) diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index cf55580be..2add7c995 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -230,8 +230,8 @@ static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jobject env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); env->GetCharArrayRegion(after, 0, afterLength, afterChars); return Correction::RankingAlgorithm::calcNormalizedScore( - reinterpret_cast(beforeChars), beforeLength, - reinterpret_cast(afterChars), afterLength, score); + static_cast(beforeChars), beforeLength, + static_cast(afterChars), afterLength, score); } static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, @@ -243,8 +243,8 @@ static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jobject object, env->GetCharArrayRegion(before, 0, beforeLength, beforeChars); env->GetCharArrayRegion(after, 0, afterLength, afterChars); return Correction::RankingAlgorithm::editDistance( - reinterpret_cast(beforeChars), beforeLength, - reinterpret_cast(afterChars), afterLength); + static_cast(beforeChars), beforeLength, + static_cast(afterChars), afterLength); } static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jlong dict) { diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp index df1ebc0e3..f1d538095 100644 --- a/native/jni/src/bigram_dictionary.cpp +++ b/native/jni/src/bigram_dictionary.cpp @@ -60,16 +60,15 @@ bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequ AKLOGI("Bigram: InsertAt -> %d MAX_PREDICTIONS: %d", insertAt, MAX_PREDICTIONS); } if (insertAt < MAX_PREDICTIONS) { - memmove(reinterpret_cast(bigramFreq) + (insertAt + 1) * sizeof(bigramFreq[0]), - reinterpret_cast(bigramFreq) + insertAt * sizeof(bigramFreq[0]), + memmove(bigramFreq + (insertAt + 1), + bigramFreq + insertAt, (MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramFreq[0])); bigramFreq[insertAt] = frequency; outputTypes[insertAt] = Dictionary::KIND_PREDICTION; - memmove(reinterpret_cast(bigramChars) - + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short), - reinterpret_cast(bigramChars) + insertAt * MAX_WORD_LENGTH * sizeof(short), - (MAX_PREDICTIONS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH); - unsigned short *dest = bigramChars + (insertAt ) * MAX_WORD_LENGTH; + memmove(bigramChars + (insertAt + 1) * MAX_WORD_LENGTH, + bigramChars + insertAt * MAX_WORD_LENGTH, + (MAX_PREDICTIONS - insertAt - 1) * sizeof(bigramChars[0]) * MAX_WORD_LENGTH); + unsigned short *dest = bigramChars + insertAt * MAX_WORD_LENGTH; while (length--) { *dest++ = *word++; } diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp index fc0a0596f..223291f60 100644 --- a/native/jni/src/char_utils.cpp +++ b/native/jni/src/char_utils.cpp @@ -885,14 +885,13 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { }; static int compare_pair_capital(const void *a, const void *b) { - return static_cast(*reinterpret_cast(a)) - - static_cast( - (reinterpret_cast(b))->capital); + return static_cast(*static_cast(a)) + - static_cast((static_cast(b))->capital); } unsigned short latin_tolower(unsigned short c) { struct LatinCapitalSmallPair *p = - reinterpret_cast(bsearch(&c, SORTED_CHAR_MAP, + static_cast(bsearch(&c, SORTED_CHAR_MAP, sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]), sizeof(SORTED_CHAR_MAP[0]), compare_pair_capital)); diff --git a/native/jni/src/debug.h b/native/jni/src/debug.h index 2432b1f2e..4e2164014 100644 --- a/native/jni/src/debug.h +++ b/native/jni/src/debug.h @@ -58,7 +58,7 @@ static inline void LOGI_S16_PLUS(unsigned short *string, const unsigned int leng } static inline void printDebug(const char *tag, int *codes, int codesSize, int MAX_PROXIMITY_CHARS) { - unsigned char *buf = reinterpret_cast(malloc((1 + codesSize) * sizeof(*buf))); + unsigned char *buf = static_cast(malloc((1 + codesSize) * sizeof(*buf))); buf[codesSize] = 0; while (--codesSize >= 0) { diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index 9e4bd15a9..158c3fb22 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -32,8 +32,8 @@ namespace latinime { Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions) - : mDict(reinterpret_cast(dict)), - mOffsetDict((reinterpret_cast(dict)) + BinaryFormat::getHeaderSize(mDict)), + : mDict(static_cast(dict)), + mOffsetDict((static_cast(dict)) + BinaryFormat::getHeaderSize(mDict)), mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) { if (DEBUG_DICT) { if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) { diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h index 2fad9bb3d..1e4e00a23 100644 --- a/native/jni/src/words_priority_queue.h +++ b/native/jni/src/words_priority_queue.h @@ -140,13 +140,12 @@ class WordsPriorityQueue { continue; } const unsigned int wordLength = sw->mWordLength; - char *targetAddress = reinterpret_cast(outputChars) - + i * MAX_WORD_LENGTH * sizeof(short); + unsigned short *targetAddress = outputChars + i * MAX_WORD_LENGTH; frequencies[i] = sw->mScore; outputTypes[i] = sw->mType; - memcpy(targetAddress, sw->mWord, (wordLength) * sizeof(short)); + memcpy(targetAddress, sw->mWord, wordLength * sizeof(unsigned short)); if (wordLength < MAX_WORD_LENGTH) { - reinterpret_cast(targetAddress)[wordLength] = 0; + targetAddress[wordLength] = 0; } sw->mUsed = false; } From 50eadf798535cee9dd367ad0faf583938356ff40 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 14 Aug 2012 10:34:14 -0700 Subject: [PATCH 052/133] Retire LOCAL_NDK_VERSION. Bug: 6932421 Change-Id: I6cb8cdb3869c9c1c3d908f9c182e6f634ae76322 --- native/jni/Android.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 1725a7638..567648f7a 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -70,7 +70,6 @@ endif # FLAG_DO_PROFILE LOCAL_MODULE := libjni_latinime_common_static LOCAL_MODULE_TAGS := optional -LOCAL_NDK_VERSION := 8 LOCAL_SDK_VERSION := 14 LOCAL_NDK_STL_VARIANT := stlport_static @@ -94,7 +93,6 @@ endif # FLAG_DO_PROFILE LOCAL_MODULE := libjni_latinime LOCAL_MODULE_TAGS := optional -LOCAL_NDK_VERSION := 8 LOCAL_SDK_VERSION := 14 LOCAL_NDK_STL_VARIANT := stlport_static From 05384933097c1e9c35e8be5c03757d072e5ffa46 Mon Sep 17 00:00:00 2001 From: alanv Date: Tue, 14 Aug 2012 15:34:29 -0700 Subject: [PATCH 053/133] Fix the build. Change-Id: I744f6232e81981ab694d3d1e92bab5fd6efa46c0 --- java/src/com/android/inputmethod/latin/LatinIME.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 4a7d0403b..446d44e7a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -667,7 +667,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Forward this event to the accessibility utilities, if enabled. final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance(); if (accessUtils.isTouchExplorationEnabled()) { - accessUtils.onStartInputViewInternal(inputView, editorInfo, restarting); + accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting); } if (!restarting) { From 3979f060f0650cbc117eee0307d05fb0be78c6f2 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Tue, 14 Aug 2012 22:49:59 +0900 Subject: [PATCH 054/133] Step 28-A Introduce the session Id to getSuggestion Change-Id: I39d9bf1a7c272eb16d6ed4698f52457579b40f10 --- .../inputmethod/latin/BinaryDictionary.java | 39 ++++++++++++++++--- .../inputmethod/latin/DicTraverseSession.java | 4 +- .../android/inputmethod/latin/Dictionary.java | 7 ++++ .../android/inputmethod/latin/Suggest.java | 16 ++++++-- 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index f0f5cd320..cdaffa655 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; +import android.util.SparseArray; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -51,6 +52,7 @@ public class BinaryDictionary extends Dictionary { private static final int TYPED_LETTER_MULTIPLIER = 2; private long mNativeDict; + private final Locale mLocale; private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; // TODO: The below should be int[] mOutputCodePoints private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS]; @@ -59,7 +61,22 @@ public class BinaryDictionary extends Dictionary { private final int[] mOutputTypes = new int[MAX_RESULTS]; private final boolean mUseFullEditDistance; - private final DicTraverseSession mDicTraverseSession; + + private final SparseArray mDicTraverseSessions = + new SparseArray(); + private DicTraverseSession getTraverseSession(int traverseSessionId) { + DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); + if (traverseSession == null) { + synchronized(mDicTraverseSessions) { + traverseSession = mDicTraverseSessions.get(traverseSessionId); + if (traverseSession == null) { + traverseSession = new DicTraverseSession(mLocale, mNativeDict); + mDicTraverseSessions.put(traverseSessionId, traverseSession); + } + } + } + return traverseSession; + } /** * Constructor for the binary dictionary. This is supposed to be called from the @@ -76,10 +93,9 @@ public class BinaryDictionary extends Dictionary { final String filename, final long offset, final long length, final boolean useFullEditDistance, final Locale locale, final String dictType) { super(dictType); + mLocale = locale; mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length); - mDicTraverseSession = new DicTraverseSession(locale); - mDicTraverseSession.initSession(mNativeDict); } static { @@ -109,7 +125,14 @@ public class BinaryDictionary extends Dictionary { @Override public ArrayList getSuggestions(final WordComposer composer, final CharSequence prevWord, final ProximityInfo proximityInfo) { + return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0); + } + + @Override + public ArrayList getSuggestionsWithSessionId(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) { if (!isValidDictionary()) return null; + Arrays.fill(mInputCodePoints, WordComposer.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWord) @@ -128,7 +151,7 @@ public class BinaryDictionary extends Dictionary { final int codesSize = isGesture ? ips.getPointerSize() : composerSize; // proximityInfo and/or prevWordForBigrams may not be null. final int tmpCount = getSuggestionsNative(mNativeDict, - proximityInfo.getNativeProximityInfo(), mDicTraverseSession.getSession(), + proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray, mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); @@ -187,7 +210,13 @@ public class BinaryDictionary extends Dictionary { @Override public synchronized void close() { - mDicTraverseSession.close(); + for (int i = 0; i < mDicTraverseSessions.size(); ++i) { + final int key = mDicTraverseSessions.keyAt(i); + final DicTraverseSession traverseSession = mDicTraverseSessions.get(key); + if (traverseSession != null) { + traverseSession.close(); + } + } closeInternal(); } diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index c76815363..359da72cc 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -22,6 +22,7 @@ public class DicTraverseSession { static { JniUtils.loadNativeLibrary(); } + private native long setDicTraverseSessionNative(String locale); private native void initDicTraverseSessionNative(long nativeDicTraverseSession, long dictionary, int[] previousWord, int previousWordLength); @@ -29,9 +30,10 @@ public class DicTraverseSession { private long mNativeDicTraverseSession; - public DicTraverseSession(Locale locale) { + public DicTraverseSession(Locale locale, long dictionary) { mNativeDicTraverseSession = createNativeDicTraverseSession( locale != null ? locale.toString() : ""); + initSession(dictionary); } public long getSession() { diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 60fe17b19..2565dfc66 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -62,6 +62,13 @@ public abstract class Dictionary { abstract public ArrayList getSuggestions(final WordComposer composer, final CharSequence prevWord, final ProximityInfo proximityInfo); + // The default implementation of this method ignores sessionId. + // Subclasses that want to use sessionId need to override this method. + public ArrayList getSuggestionsWithSessionId(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) { + return getSuggestions(composer, prevWord, proximityInfo); + } + /** * Checks if the given word occurs in the dictionary * @param word the word to search for. The search should be case-insensitive. diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 8a2341d5e..1f43c6d85 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -169,9 +169,17 @@ public class Suggest { public SuggestedWords getSuggestedWords( final WordComposer wordComposer, CharSequence prevWordForBigram, final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { + return getSuggestedWordsWithSessionId( + wordComposer, prevWordForBigram, proximityInfo, isCorrectionEnabled, 0); + } + + public SuggestedWords getSuggestedWordsWithSessionId( + final WordComposer wordComposer, CharSequence prevWordForBigram, + final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) { LatinImeLogger.onStartSuggestion(prevWordForBigram); if (wordComposer.isBatchMode()) { - return getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo); + return getSuggestedWordsForBatchInput( + wordComposer, prevWordForBigram, proximityInfo, sessionId); } else { return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, isCorrectionEnabled); @@ -306,7 +314,7 @@ public class Suggest { // Retrieves suggestions for the batch input. private SuggestedWords getSuggestedWordsForBatchInput( final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo) { + final ProximityInfo proximityInfo, int sessionId) { final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS); @@ -318,8 +326,8 @@ public class Suggest { continue; } final Dictionary dictionary = mDictionaries.get(key); - suggestionsSet.addAll(dictionary.getSuggestions( - wordComposer, prevWordForBigram, proximityInfo)); + suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId( + wordComposer, prevWordForBigram, proximityInfo, sessionId)); } final ArrayList suggestionsContainer = From f6be15cffee14b35efce8a52296d7bf8e338b234 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Wed, 15 Aug 2012 16:57:32 +0900 Subject: [PATCH 055/133] Step 30-A Constize gesture Change-Id: I6c0b89d96532dc1c8ca71bb730ba22de5592ec67 --- native/jni/src/dic_traverse_wrapper.cpp | 4 ++-- native/jni/src/dic_traverse_wrapper.h | 9 +++++---- native/jni/src/dictionary.cpp | 2 +- native/jni/src/dictionary.h | 2 +- native/jni/src/gesture/gesture_decoder_wrapper.h | 3 ++- native/jni/src/gesture/incremental_decoder_interface.h | 2 +- native/jni/src/gesture/incremental_decoder_wrapper.h | 3 ++- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/native/jni/src/dic_traverse_wrapper.cpp b/native/jni/src/dic_traverse_wrapper.cpp index 1f7dcbfb2..88ca9fa0d 100644 --- a/native/jni/src/dic_traverse_wrapper.cpp +++ b/native/jni/src/dic_traverse_wrapper.cpp @@ -19,8 +19,8 @@ #include "dic_traverse_wrapper.h" namespace latinime { -void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *env, jstring locale) = 0; +void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring) = 0; void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)( - void *, Dictionary *, const int *, const int) = 0; + void *, const Dictionary *const, const int *, const int) = 0; } // namespace latinime diff --git a/native/jni/src/dic_traverse_wrapper.h b/native/jni/src/dic_traverse_wrapper.h index 8396d0027..292382487 100644 --- a/native/jni/src/dic_traverse_wrapper.h +++ b/native/jni/src/dic_traverse_wrapper.h @@ -34,7 +34,7 @@ class DicTraverseWrapper { return 0; } static void initDicTraverseSession(void *traverseSession, - Dictionary *dictionary, const int *prevWord, const int prevWordLength) { + const Dictionary *const dictionary, const int *prevWord, const int prevWordLength) { if (sDicTraverseSessionInitMethod) { sDicTraverseSessionInitMethod(traverseSession, dictionary, prevWord, prevWordLength); } @@ -45,11 +45,11 @@ class DicTraverseWrapper { } } static void setTraverseSessionFactoryMethod( - void *(*factoryMethod)(JNIEnv *env, jstring locale)) { + void *(*factoryMethod)(JNIEnv *, jstring)) { sDicTraverseSessionFactoryMethod = factoryMethod; } static void setTraverseSessionInitMethod( - void (*initMethod)(void *, Dictionary *, const int *, const int)) { + void (*initMethod)(void *, const Dictionary *const, const int *, const int)) { sDicTraverseSessionInitMethod = initMethod; } static void setTraverseSessionReleaseMethod(void (*releaseMethod)(void *)) { @@ -58,7 +58,8 @@ class DicTraverseWrapper { private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); static void *(*sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring); - static void (*sDicTraverseSessionInitMethod)(void *, Dictionary *, const int *, const int); + static void (*sDicTraverseSessionInitMethod)( + void *, const Dictionary *const, const int *, const int); static void (*sDicTraverseSessionReleaseMethod)(void *); }; int register_DicTraverseSession(JNIEnv *env); diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index 158c3fb22..8ce8c8ba6 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -60,7 +60,7 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSessi int *codes, int codesSize, int *prevWordChars, int prevWordLength, int commitPoint, bool isGesture, bool useFullEditDistance, unsigned short *outWords, - int *frequencies, int *spaceIndices, int *outputTypes) { + int *frequencies, int *spaceIndices, int *outputTypes) const { int result = 0; if (isGesture) { DicTraverseWrapper::initDicTraverseSession( diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h index fd9e77011..e9a03ce55 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/dictionary.h @@ -48,7 +48,7 @@ class Dictionary { int *ycoordinates, int *times, int *pointerIds, int *codes, int codesSize, int *prevWordChars, int prevWordLength, int commitPoint, bool isGesture, bool useFullEditDistance, unsigned short *outWords, - int *frequencies, int *spaceIndices, int *outputTypes); + int *frequencies, int *spaceIndices, int *outputTypes) const; int getBigrams(const int32_t *word, int length, int *codes, int codesSize, unsigned short *outWords, int *frequencies, int *outputTypes) const; diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index f8bfe7c79..2f6220b92 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -39,7 +39,8 @@ class GestureDecoderWrapper : public IncrementalDecoderInterface { int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, - unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) { + unsigned short *outWords, int *frequencies, int *outputIndices, + int *outputTypes) const { if (!mIncrementalDecoderInterface) { return 0; } diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h index 04f0095e0..d1395aab9 100644 --- a/native/jni/src/gesture/incremental_decoder_interface.h +++ b/native/jni/src/gesture/incremental_decoder_interface.h @@ -31,7 +31,7 @@ class IncrementalDecoderInterface { virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, unsigned short *outWords, int *frequencies, - int *outputIndices, int *outputTypes) = 0; + int *outputIndices, int *outputTypes) const = 0; IncrementalDecoderInterface() { }; virtual ~IncrementalDecoderInterface() { }; private: diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 5cb2ee368..1f4d8a047 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -39,7 +39,8 @@ class IncrementalDecoderWrapper : public IncrementalDecoderInterface { int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint, - unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) { + unsigned short *outWords, int *frequencies, int *outputIndices, + int *outputTypes) const { if (!mIncrementalDecoderInterface) { return 0; } From 653dbfb8ed4dfa17b2d10865a04185b1e15f4e43 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 15 Aug 2012 16:09:38 +0900 Subject: [PATCH 056/133] Ensure #offer always actually offers something. This will allow remaining requests after a call to terminate correctly. Bug: 6963142 Change-Id: Iff67058bb8a39f2f1b468d3894861e8125de6659 --- .../AndroidWordLevelSpellCheckerSession.java | 4 +-- .../latin/spellcheck/DictionaryPool.java | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 06f5db749..317fe7cd3 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -194,7 +194,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); - if (null == dictInfo) { + if (!DictionaryPool.isAValidDictionary(dictInfo)) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } return dictInfo.mDictionary.isValidWord(inText) @@ -237,7 +237,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); - if (null == dictInfo) { + if (!DictionaryPool.isAValidDictionary(dictInfo)) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } final ArrayList suggestions = diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 83f82faeb..218eab7de 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -18,6 +18,12 @@ package com.android.inputmethod.latin.spellcheck; import android.util.Log; +import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.WordComposer; + +import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -39,6 +45,26 @@ public class DictionaryPool extends LinkedBlockingQueue { private final Locale mLocale; private int mSize; private volatile boolean mClosed; + final static ArrayList noSuggestions = new ArrayList(); + private final static DictAndProximity dummyDict = new DictAndProximity( + new Dictionary(Dictionary.TYPE_MAIN) { + @Override + public ArrayList getSuggestions(final WordComposer composer, + final CharSequence prevWord, final ProximityInfo proximityInfo) { + return noSuggestions; + } + @Override + public boolean isValidWord(CharSequence word) { + // This is never called. However if for some strange reason it ever gets + // called, returning true is less destructive (it will not underline the + // word in red). + return true; + } + }, null); + + static public boolean isAValidDictionary(final DictAndProximity dictInfo) { + return null != dictInfo && dummyDict != dictInfo; + } public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, final Locale locale) { @@ -98,7 +124,7 @@ public class DictionaryPool extends LinkedBlockingQueue { public boolean offer(final DictAndProximity dict) { if (mClosed) { dict.mDictionary.close(); - return false; + return super.offer(dummyDict); } else { return super.offer(dict); } From e234aed4288efd7b4336f3755a958c25a1540b98 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 15 Aug 2012 17:57:38 +0900 Subject: [PATCH 057/133] Refresh editor info when the text field attributes changes. For some reason we get a "restarting" flag from the browser some times when the input field is different. The attributes however are not the same, so we can detect that and reload appropriately. Bug: 6946793 Change-Id: I6762dae6f41db690497b026a707d9cc89c840b34 --- .../android/inputmethod/latin/InputAttributes.java | 6 ++++++ java/src/com/android/inputmethod/latin/LatinIME.java | 12 +++++++----- .../android/inputmethod/latin/SettingsValues.java | 4 ++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index e561f5956..7bcda9bc4 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -29,10 +29,12 @@ public class InputAttributes { final public boolean mInputTypeNoAutoCorrect; final public boolean mIsSettingsSuggestionStripOn; final public boolean mApplicationSpecifiedCompletionOn; + final private int mInputType; public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { final int inputType = null != editorInfo ? editorInfo.inputType : 0; final int inputClass = inputType & InputType.TYPE_MASK_CLASS; + mInputType = inputType; if (inputClass != InputType.TYPE_CLASS_TEXT) { // If we are not looking at a TYPE_CLASS_TEXT field, the following strange // cases may arise, so we do a couple sanity checks for them. If it's a @@ -93,6 +95,10 @@ public class InputAttributes { } } + public boolean isSameInputType(final EditorInfo editorInfo) { + return editorInfo.inputType == mInputType; + } + @SuppressWarnings("unused") private void dumpFlags(final int inputType) { Log.i(TAG, "Input class:"); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 446d44e7a..884e6db29 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -670,7 +670,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting); } - if (!restarting) { + final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart + || mLastSelectionEnd != editorInfo.initialSelEnd; + final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo); + final boolean isDifferentTextField = !restarting || inputTypeChanged; + if (isDifferentTextField) { mSubtypeSwitcher.updateParametersOnStartInputView(); } @@ -679,9 +683,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen updateFullscreenMode(); mApplicationSpecifiedCompletions = null; - final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart - || mLastSelectionEnd != editorInfo.initialSelEnd; - if (!restarting || selectionChanged) { + if (isDifferentTextField || selectionChanged) { // If the selection changed, we reset the input state. Essentially, we come here with // restarting == true when the app called setText() or similar. We should reset the // state if the app set the text to something else, but keep it if it set a suggestion @@ -696,7 +698,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - if (!restarting) { + if (isDifferentTextField) { mainKeyboardView.closing(); loadSettings(); diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 0843bdbbc..c8755be9d 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -417,6 +417,10 @@ public class SettingsValues { prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply(); } + public boolean isSameInputType(final EditorInfo editorInfo) { + return mInputAttributes.isSameInputType(editorInfo); + } + // For debug. public String getInputAttributesDebugString() { return mInputAttributes.toString(); From fee0ac60b1cd0a4760ca8f310ff8a86b925d833b Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Thu, 16 Aug 2012 19:34:02 +0900 Subject: [PATCH 058/133] Cleanup and fix method visibility. Change-Id: Ia9e8c69da21ce22bf674ec6c7b2536008a360ea3 --- native/jni/src/correction.cpp | 2 +- native/jni/src/correction.h | 154 +++++++++++++------------- native/jni/src/proximity_info.h | 24 ++-- native/jni/src/proximity_info_state.h | 7 +- 4 files changed, 92 insertions(+), 95 deletions(-) diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index e55da0113..7513b307d 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -230,7 +230,7 @@ int Correction::goDownTree( } // TODO: remove -int Correction::getInputIndex() { +int Correction::getInputIndex() const { return mInputIndex; } diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h index 57e7b7189..9899f6e66 100644 --- a/native/jni/src/correction.h +++ b/native/jni/src/correction.h @@ -38,63 +38,8 @@ class Correction { NOT_ON_TERMINAL } CorrectionType; - ///////////////////////// - // static inline utils // - ///////////////////////// - - static const int TWO_31ST_DIV_255 = S_INT_MAX / 255; - static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) { - return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX); - } - - static const int TWO_31ST_DIV_2 = S_INT_MAX / 2; - inline static void multiplyIntCapped(const int multiplier, int *base) { - const int temp = *base; - if (temp != S_INT_MAX) { - // Branch if multiplier == 2 for the optimization - if (multiplier < 0) { - if (DEBUG_DICT) { - assert(false); - } - AKLOGI("--- Invalid multiplier: %d", multiplier); - } else if (multiplier == 0) { - *base = 0; - } else if (multiplier == 2) { - *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX; - } else { - // TODO: This overflow check gives a wrong answer when, for example, - // temp = 2^16 + 1 and multiplier = 2^17 + 1. - // Fix this behavior. - const int tempRetval = temp * multiplier; - *base = tempRetval >= temp ? tempRetval : S_INT_MAX; - } - } - } - - inline static int powerIntCapped(const int base, const int n) { - if (n <= 0) return 1; - if (base == 2) { - return n < 31 ? 1 << n : S_INT_MAX; - } else { - int ret = base; - for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret); - return ret; - } - } - - inline static void multiplyRate(const int rate, int *freq) { - if (*freq != S_INT_MAX) { - if (*freq > 1000000) { - *freq /= 100; - multiplyIntCapped(rate, freq); - } else { - multiplyIntCapped(rate, freq); - *freq /= 100; - } - } - } - Correction() {}; + virtual ~Correction(); void resetCorrection(); void initCorrection( const ProximityInfo *pi, const int inputLength, const int maxWordLength); @@ -108,27 +53,7 @@ class Correction { bool sameAsTyped(); bool initProcessState(const int index); - int getInputIndex(); - - virtual ~Correction(); - int getSpaceProximityPos() const { - return mSpaceProximityPos; - } - int getMissingSpacePos() const { - return mMissingSpacePos; - } - - int getSkipPos() const { - return mSkipPos; - } - - int getExcessivePos() const { - return mExcessivePos; - } - - int getTransposedPos() const { - return mTransposedPos; - } + int getInputIndex() const; bool needsToPrune() const; @@ -195,6 +120,81 @@ class Correction { private: DISALLOW_COPY_AND_ASSIGN(Correction); + + ///////////////////////// + // static inline utils // + ///////////////////////// + static const int TWO_31ST_DIV_255 = S_INT_MAX / 255; + static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) { + return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX); + } + + static const int TWO_31ST_DIV_2 = S_INT_MAX / 2; + inline static void multiplyIntCapped(const int multiplier, int *base) { + const int temp = *base; + if (temp != S_INT_MAX) { + // Branch if multiplier == 2 for the optimization + if (multiplier < 0) { + if (DEBUG_DICT) { + assert(false); + } + AKLOGI("--- Invalid multiplier: %d", multiplier); + } else if (multiplier == 0) { + *base = 0; + } else if (multiplier == 2) { + *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX; + } else { + // TODO: This overflow check gives a wrong answer when, for example, + // temp = 2^16 + 1 and multiplier = 2^17 + 1. + // Fix this behavior. + const int tempRetval = temp * multiplier; + *base = tempRetval >= temp ? tempRetval : S_INT_MAX; + } + } + } + + inline static int powerIntCapped(const int base, const int n) { + if (n <= 0) return 1; + if (base == 2) { + return n < 31 ? 1 << n : S_INT_MAX; + } else { + int ret = base; + for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret); + return ret; + } + } + + inline static void multiplyRate(const int rate, int *freq) { + if (*freq != S_INT_MAX) { + if (*freq > 1000000) { + *freq /= 100; + multiplyIntCapped(rate, freq); + } else { + multiplyIntCapped(rate, freq); + *freq /= 100; + } + } + } + + inline int getSpaceProximityPos() const { + return mSpaceProximityPos; + } + inline int getMissingSpacePos() const { + return mMissingSpacePos; + } + + inline int getSkipPos() const { + return mSkipPos; + } + + inline int getExcessivePos() const { + return mExcessivePos; + } + + inline int getTransposedPos() const { + return mTransposedPos; + } + inline void incrementInputIndex(); inline void incrementOutputIndex(); inline void startToTraverseAllNodes(); diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 8a407e71a..7ee633ba8 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -41,21 +41,12 @@ class ProximityInfo { float getNormalizedSquaredDistanceFromCenterFloat( const int keyId, const int x, const int y) const; bool sameAsTyped(const unsigned short *word, int length) const; - int squaredDistanceToEdge(const int keyId, const int x, const int y) const; - bool isOnKey(const int keyId, const int x, const int y) const { - if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case - const int left = mKeyXCoordinates[keyId]; - const int top = mKeyYCoordinates[keyId]; - const int right = left + mKeyWidths[keyId] + 1; - const int bottom = top + mKeyHeights[keyId]; - return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom; - } int getKeyIndex(const int c) const; int getKeyCode(const int keyIndex) const; bool hasSweetSpotData(const int keyIndex) const { // When there are no calibration data for a key, // the radius of the key is assigned to zero. - return mSweetSpotRadii[keyIndex] > 0.0; + return mSweetSpotRadii[keyIndex] > 0.0f; } float getSweetSpotRadiiAt(int keyIndex) const { return mSweetSpotRadii[keyIndex]; @@ -111,10 +102,6 @@ class ProximityInfo { float getKeyCenterYOfIdG(int keyId) const; int getKeyKeyDistanceG(int key0, int key1) const; - // Returns the keyboard key-center information. - void getCenters(int *centersX, int *centersY, int *codeToKeyIndex, int *keyToCodeIndex, - int *keyCount, int *keyWidth) const; - private: DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo); // The max number of the keys in one keyboard layout @@ -131,6 +118,15 @@ class ProximityInfo { float calculateSquaredDistanceFromSweetSpotCenter( const int keyIndex, const int inputIndex) const; bool hasInputCoordinates() const; + int squaredDistanceToEdge(const int keyId, const int x, const int y) const; + bool isOnKey(const int keyId, const int x, const int y) const { + if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case + const int left = mKeyXCoordinates[keyId]; + const int top = mKeyYCoordinates[keyId]; + const int right = left + mKeyWidths[keyId] + 1; + const int bottom = top + mKeyHeights[keyId]; + return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom; + } const int MAX_PROXIMITY_CHARS_SIZE; const int KEYBOARD_WIDTH; diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 474c40757..2fb0ae044 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -48,9 +48,6 @@ class ProximityInfoState { // Defined here // ///////////////////////////////////////// ProximityInfoState() {}; - inline const int *getProximityCharsAt(const int index) const { - return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL); - } inline unsigned short getPrimaryCharAt(const int index) const { return getProximityCharsAt(index)[0]; @@ -194,6 +191,10 @@ class ProximityInfoState { return mInputXCoordinates && mInputYCoordinates; } + inline const int *getProximityCharsAt(const int index) const { + return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL); + } + // const const ProximityInfo *mProximityInfo; bool mHasTouchPositionCorrectionData; From 66f338983bb9cb04a0d94a4729330b1c8ff01c93 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Thu, 16 Aug 2012 19:23:56 +0900 Subject: [PATCH 059/133] fix findWordInTree. Change-Id: I9d81c815494a0670afa81219ad7bad82274d997e --- .../latin/makedict/FusionDictionary.java | 11 ++++- .../latin/FusionDictionaryTests.java | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 5864db28e..260155174 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -516,13 +516,22 @@ public class FusionDictionary implements Iterable { int indexOfGroup = findIndexOfChar(node, s.codePointAt(index)); if (CHARACTER_NOT_FOUND == indexOfGroup) return null; currentGroup = node.mData.get(indexOfGroup); + + if (s.length() - index < currentGroup.mChars.length) return null; + int newIndex = index; + while (newIndex < s.length() && newIndex - index < currentGroup.mChars.length) { + if (currentGroup.mChars[newIndex - index] != s.codePointAt(newIndex)) return null; + newIndex++; + } + index = newIndex; + if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length)); - index += currentGroup.mChars.length; if (index < s.length()) { node = currentGroup.mChildren; } } while (null != node && index < s.length()); + if (index < s.length()) return null; if (DBG && !s.equals(checker.toString())) return null; return currentGroup; } diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java new file mode 100644 index 000000000..e3f75839e --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java @@ -0,0 +1,42 @@ +/* + * 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 android.test.AndroidTestCase; + +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.Node; + +import java.util.HashMap; + +/** + * Unit test for FusionDictionary + */ +public class FusionDictionaryTests extends AndroidTestCase { + public void testFindWordInTree() { + FusionDictionary dict = new FusionDictionary(new Node(), + new FusionDictionary.DictionaryOptions(new HashMap(), false, false)); + + dict.add("abc", 10, null); + assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "abc")); + + dict.add("aa", 10, null); + assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "aa")); + } +} From a07590a81c270c5843237f8edb4f58f6f4fabd8a Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Thu, 16 Aug 2012 15:17:17 -0700 Subject: [PATCH 060/133] Import translations. DO NOT MERGE Change-Id: I5d02e55780596d25356a602027f3c41913057810 Auto-generated-cl: translation import --- java/res/values-is/strings.xml | 263 +++++++++++++++++++++++++++++++++ java/res/values-ka/strings.xml | 263 +++++++++++++++++++++++++++++++++ java/res/values-mk/strings.xml | 263 +++++++++++++++++++++++++++++++++ 3 files changed, 789 insertions(+) create mode 100644 java/res/values-is/strings.xml create mode 100644 java/res/values-ka/strings.xml create mode 100644 java/res/values-mk/strings.xml diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml new file mode 100644 index 000000000..8d5b00766 --- /dev/null +++ b/java/res/values-is/strings.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Áfram" + "Næsta" + "Fyrra" + "Lokið" + "Senda" + "ABC" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml new file mode 100644 index 000000000..fcb666d57 --- /dev/null +++ b/java/res/values-ka/strings.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "გადასვლა" + "შემდეგი" + "წინა" + "შესრულებულია" + "გაგზავნა" + "ABC" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml new file mode 100644 index 000000000..7f293e4dc --- /dev/null +++ b/java/res/values-mk/strings.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Оди" + "Следно" + "Претходно" + "Готово" + "Испрати" + "АБВ" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cc1062cfd06fe2e79c7bfc9318be6eb68da811dd Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 09:24:45 +0900 Subject: [PATCH 061/133] Fix a debug method Change-Id: Icf78fb0d5f7caea78f053fffd1ecd4289629964a --- java/src/com/android/inputmethod/latin/BinaryDictionary.java | 2 +- native/jni/src/debug.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index cdaffa655..726c44cc1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -162,7 +162,7 @@ public class BinaryDictionary extends Dictionary { if (composerSize > 0 && mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; int len = 0; - while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { + while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { ++len; } if (len > 0) { diff --git a/native/jni/src/debug.h b/native/jni/src/debug.h index 4e2164014..8f6b69d77 100644 --- a/native/jni/src/debug.h +++ b/native/jni/src/debug.h @@ -22,7 +22,7 @@ static inline unsigned char *convertToUnibyteString(unsigned short *input, unsigned char *output, const unsigned int length) { unsigned int i = 0; - for (; i <= length && input[i] != 0; ++i) + for (; i < length && input[i] != 0; ++i) output[i] = input[i] & 0xFF; output[i] = 0; return output; @@ -31,7 +31,7 @@ static inline unsigned char *convertToUnibyteString(unsigned short *input, unsig static inline unsigned char *convertToUnibyteStringAndReplaceLastChar(unsigned short *input, unsigned char *output, const unsigned int length, unsigned char c) { unsigned int i = 0; - for (; i <= length && input[i] != 0; ++i) + for (; i < length && input[i] != 0; ++i) output[i] = input[i] & 0xFF; if (i > 0) output[i-1] = c; output[i] = 0; From 0d35c159fefd7591c2ab9d5037c32d1804024197 Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Fri, 17 Aug 2012 10:23:01 +0900 Subject: [PATCH 062/133] fix findWordInTree. Change-Id: I8f42df28f76188677db9d4e55885e1fc6a40b53f --- .../inputmethod/latin/makedict/FusionDictionary.java | 1 + .../android/inputmethod/latin/FusionDictionaryTests.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 260155174..7c15ba54d 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -532,6 +532,7 @@ public class FusionDictionary implements Iterable { } while (null != node && index < s.length()); if (index < s.length()) return null; + if (!currentGroup.isTerminal()) return null; if (DBG && !s.equals(checker.toString())) return null; return currentGroup; } diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java index e3f75839e..8ecdcc366 100644 --- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java @@ -38,5 +38,11 @@ public class FusionDictionaryTests extends AndroidTestCase { dict.add("aa", 10, null); assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa")); assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "aa")); + + dict.add("babcd", 10, null); + dict.add("bacde", 10, null); + assertNull(FusionDictionary.findWordInTree(dict.mRoot, "ba")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "babcd")); + assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "bacde")); } } From 8af8c15c6ba370ef3c0e9083e5229b4080ea288f Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 09:37:39 +0900 Subject: [PATCH 063/133] Allow one-character suggestions. Bug: 6906525 Change-Id: I62117653ac1281f825afc4ebb30f7e786902b750 --- ...oid_inputmethod_latin_BinaryDictionary.cpp | 2 +- native/jni/src/correction.cpp | 4 -- native/jni/src/defines.h | 2 - native/jni/src/unigram_dictionary.cpp | 69 +++++++++---------- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 2add7c995..6a0a7c2a0 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -178,7 +178,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, memset(outputTypes, 0, outputTypesLength * sizeof(outputTypes[0])); int count; - if (isGesture || arraySize > 1) { + if (isGesture || arraySize > 0) { count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, times, pointerIds, inputCodePoints, arraySize, prevWordCodePoints, prevWordCodePointsLength, commitPoint, isGesture, useFullEditDistance, outputChars, diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index 7513b307d..71f8a4fc8 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -181,10 +181,6 @@ int Correction::getFinalProbabilityInternal(const int probability, unsigned shor const int outputIndex = mTerminalOutputIndex; const int inputIndex = mTerminalInputIndex; *wordLength = outputIndex + 1; - if (outputIndex < MIN_SUGGEST_DEPTH) { - return NOT_A_PROBABILITY; - } - *word = mWord; int finalProbability= Correction::RankingAlgorithm::calculateFinalProbability( inputIndex, outputIndex, probability, mEditDistanceTable, this, inputLength); diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 484fc6bde..929b303d6 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -298,8 +298,6 @@ static inline void prof_out(void) { // word in the dictionary for languages with digraphs, like German and French #define DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH 5 -// Minimum suggest depth for one word for all cases except for missing space suggestions. -#define MIN_SUGGEST_DEPTH 1 #define MIN_USER_TYPED_LENGTH_FOR_MULTIPLE_WORD_SUGGESTION 3 #define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3 diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index cc6d39a29..705defc00 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -390,43 +390,42 @@ inline void UnigramDictionary::onTerminal(const int probability, WordsPriorityQueue *masterQueue = queuePool->getMasterQueue(); const int finalProbability = correction->getFinalProbability(probability, &wordPointer, &wordLength); - if (finalProbability != NOT_A_PROBABILITY) { - if (0 != finalProbability) { - // If the probability is 0, we don't want to add this word. However we still - // want to add its shortcuts (including a possible whitelist entry) if any. - addWord(wordPointer, wordLength, finalProbability, masterQueue, - Dictionary::KIND_CORRECTION); - } - const int shortcutProbability = finalProbability > 0 ? finalProbability - 1 : 0; - // Please note that the shortcut candidates will be added to the master queue only. - TerminalAttributes::ShortcutIterator iterator = - terminalAttributes.getShortcutIterator(); - while (iterator.hasNextShortcutTarget()) { - // TODO: addWord only supports weak ordering, meaning we have no means - // to control the order of the shortcuts relative to one another or to the word. - // We need to either modulate the probability of each shortcut according - // to its own shortcut probability or to make the queue - // so that the insert order is protected inside the queue for words - // with the same score. For the moment we use -1 to make sure the shortcut will - // never be in front of the word. - uint16_t shortcutTarget[MAX_WORD_LENGTH_INTERNAL]; - int shortcutFrequency; - const int shortcutTargetStringLength = iterator.getNextShortcutTarget( - MAX_WORD_LENGTH_INTERNAL, shortcutTarget, &shortcutFrequency); - int shortcutScore; - int kind; - if (shortcutFrequency == BinaryFormat::WHITELIST_SHORTCUT_FREQUENCY - && correction->sameAsTyped()) { - shortcutScore = S_INT_MAX; - kind = Dictionary::KIND_WHITELIST; - } else { - shortcutScore = shortcutProbability; - kind = Dictionary::KIND_CORRECTION; - } - addWord(shortcutTarget, shortcutTargetStringLength, shortcutScore, - masterQueue, kind); + if (0 != finalProbability) { + // If the probability is 0, we don't want to add this word. However we still + // want to add its shortcuts (including a possible whitelist entry) if any. + addWord(wordPointer, wordLength, finalProbability, masterQueue, + Dictionary::KIND_CORRECTION); + } + + const int shortcutProbability = finalProbability > 0 ? finalProbability - 1 : 0; + // Please note that the shortcut candidates will be added to the master queue only. + TerminalAttributes::ShortcutIterator iterator = + terminalAttributes.getShortcutIterator(); + while (iterator.hasNextShortcutTarget()) { + // TODO: addWord only supports weak ordering, meaning we have no means + // to control the order of the shortcuts relative to one another or to the word. + // We need to either modulate the probability of each shortcut according + // to its own shortcut probability or to make the queue + // so that the insert order is protected inside the queue for words + // with the same score. For the moment we use -1 to make sure the shortcut will + // never be in front of the word. + uint16_t shortcutTarget[MAX_WORD_LENGTH_INTERNAL]; + int shortcutFrequency; + const int shortcutTargetStringLength = iterator.getNextShortcutTarget( + MAX_WORD_LENGTH_INTERNAL, shortcutTarget, &shortcutFrequency); + int shortcutScore; + int kind; + if (shortcutFrequency == BinaryFormat::WHITELIST_SHORTCUT_FREQUENCY + && correction->sameAsTyped()) { + shortcutScore = S_INT_MAX; + kind = Dictionary::KIND_WHITELIST; + } else { + shortcutScore = shortcutProbability; + kind = Dictionary::KIND_CORRECTION; } + addWord(shortcutTarget, shortcutTargetStringLength, shortcutScore, + masterQueue, kind); } } From 3e43e6998e03cbdfc5a67c42003afcd36891c02f Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 10:35:37 +0900 Subject: [PATCH 064/133] Remove access to the whitelist class. ...which becomes useless. It will be entirely removed in an upcoming change. Change-Id: I2f1d27f1eceda79454b01f21e057b98f2953131a --- java/src/com/android/inputmethod/latin/Suggest.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 1f43c6d85..1f0bd75cb 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -216,15 +216,6 @@ public class Suggest { wordComposerForLookup, prevWordForBigram, proximityInfo)); } - final CharSequence whitelistedWordFromWhitelistDictionary = - mWhiteListDictionary.getWhitelistedWord(consideredWord); - if (whitelistedWordFromWhitelistDictionary != null) { - // MAX_SCORE ensures this will be considered strong enough to be auto-corrected - suggestionsSet.add(new SuggestedWordInfo(whitelistedWordFromWhitelistDictionary, - SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST, - Dictionary.TYPE_WHITELIST)); - } - final CharSequence whitelistedWord; if (suggestionsSet.isEmpty()) { whitelistedWord = null; From de8a9a8227a7a558720edba9d57fa7a056417eb4 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 17 Aug 2012 13:06:28 +0900 Subject: [PATCH 065/133] Small cleanups Change-Id: Ib66507b8934bc8019a762d24d5311411e044ec84 --- native/jni/src/bigram_dictionary.h | 4 ++-- native/jni/src/binary_format.h | 15 +++++++-------- native/jni/src/char_utils.cpp | 2 +- native/jni/src/char_utils.h | 12 +++++++----- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/native/jni/src/bigram_dictionary.h b/native/jni/src/bigram_dictionary.h index d676cca63..5f11ae822 100644 --- a/native/jni/src/bigram_dictionary.h +++ b/native/jni/src/bigram_dictionary.h @@ -29,8 +29,6 @@ class BigramDictionary { BigramDictionary(const unsigned char *dict, int maxWordLength, int maxPredictions); int getBigrams(const int32_t *word, int length, int *inputCodes, int codesSize, unsigned short *outWords, int *frequencies, int *outputTypes) const; - int getBigramListPositionForWord(const int32_t *prevWord, const int prevWordLength, - const bool forceLowerCaseSearch) const; void fillBigramAddressToFrequencyMapAndFilter(const int32_t *prevWord, const int prevWordLength, std::map *map, uint8_t *filter) const; bool isValidBigram(const int32_t *word1, int length1, const int32_t *word2, int length2) const; @@ -45,6 +43,8 @@ class BigramDictionary { bool getFirstBitOfByte(int *pos) { return (DICT[*pos] & 0x80) > 0; } bool getSecondBitOfByte(int *pos) { return (DICT[*pos] & 0x40) > 0; } bool checkFirstCharacter(unsigned short *word, int *inputCodes) const; + int getBigramListPositionForWord(const int32_t *prevWord, const int prevWordLength, + const bool forceLowerCaseSearch) const; const unsigned char *DICT; const int MAX_WORD_LENGTH; diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index 4cabc8404..d8f3e83dd 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -61,13 +61,6 @@ class BinaryFormat { static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20; static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30; - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryFormat); - const static int32_t MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; - const static int32_t CHARACTER_ARRAY_TERMINATOR = 0x1F; - const static int MULTIPLE_BYTE_CHARACTER_ADDITIONAL_SIZE = 2; - - public: const static int UNKNOWN_FORMAT = -1; // Originally, format version 1 had a 16-bit magic number, then the version number `01' // then options that must be 0. Hence the first 32-bits of the format are always as follow @@ -94,7 +87,6 @@ class BinaryFormat { static int skipFrequency(const uint8_t flags, const int pos); static int skipShortcuts(const uint8_t *const dict, const uint8_t flags, const int pos); static int skipBigrams(const uint8_t *const dict, const uint8_t flags, const int pos); - static int skipAllAttributes(const uint8_t *const dict, const uint8_t flags, const int pos); static int skipChildrenPosAndAttributes(const uint8_t *const dict, const uint8_t flags, const int pos); static int readChildrenPosition(const uint8_t *const dict, const uint8_t flags, const int pos); @@ -118,6 +110,13 @@ class BinaryFormat { REQUIRES_FRENCH_LIGATURES_PROCESSING = 0x4 }; const static unsigned int NO_FLAGS = 0; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryFormat); + const static int32_t MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20; + const static int32_t CHARACTER_ARRAY_TERMINATOR = 0x1F; + const static int MULTIPLE_BYTE_CHARACTER_ADDITIONAL_SIZE = 2; + static int skipAllAttributes(const uint8_t *const dict, const uint8_t flags, const int pos); }; inline int BinaryFormat::detectFormat(const uint8_t *const dict) { diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp index 223291f60..9d886da31 100644 --- a/native/jni/src/char_utils.cpp +++ b/native/jni/src/char_utils.cpp @@ -889,7 +889,7 @@ static int compare_pair_capital(const void *a, const void *b) { - static_cast((static_cast(b))->capital); } -unsigned short latin_tolower(unsigned short c) { +unsigned short latin_tolower(const unsigned short c) { struct LatinCapitalSmallPair *p = static_cast(bsearch(&c, SORTED_CHAR_MAP, sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]), diff --git a/native/jni/src/char_utils.h b/native/jni/src/char_utils.h index edd96bbb0..b30677fa7 100644 --- a/native/jni/src/char_utils.h +++ b/native/jni/src/char_utils.h @@ -17,21 +17,23 @@ #ifndef LATINIME_CHAR_UTILS_H #define LATINIME_CHAR_UTILS_H +#include + namespace latinime { -inline static int isAsciiUpper(unsigned short c) { - return c >= 'A' && c <= 'Z'; +inline static bool isAsciiUpper(unsigned short c) { + return isupper(static_cast(c)) != 0; } inline static unsigned short toAsciiLower(unsigned short c) { return c - 'A' + 'a'; } -inline static int isAscii(unsigned short c) { - return c <= 127; +inline static bool isAscii(unsigned short c) { + return isascii(static_cast(c)) != 0; } -unsigned short latin_tolower(unsigned short c); +unsigned short latin_tolower(const unsigned short c); /** * Table mapping most combined Latin, Greek, and Cyrillic characters From 46fc768e54e3d52003645494552f9e686f28f20f Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 14:13:13 +0900 Subject: [PATCH 066/133] Remove the whitelist dictionary. The functionality now lives in the binary dictionary. This finalizes work on Bug: 6906525 Change-Id: Id106d871e8afdf9afa886d2a30bea87ff89f2d24 --- .../inputmethod/latin/AutoCorrection.java | 2 - .../android/inputmethod/latin/Dictionary.java | 1 - .../android/inputmethod/latin/Suggest.java | 23 +--- .../latin/WhitelistDictionary.java | 119 ------------------ .../AndroidSpellCheckerService.java | 14 --- 5 files changed, 4 insertions(+), 155 deletions(-) delete mode 100644 java/src/com/android/inputmethod/latin/WhitelistDictionary.java diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index 048166807..01ba30077 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -39,7 +39,6 @@ public class AutoCorrection { } final CharSequence lowerCasedWord = word.toString().toLowerCase(); for (final String key : dictionaries.keySet()) { - if (key.equals(Dictionary.TYPE_WHITELIST)) continue; final Dictionary dictionary = dictionaries.get(key); // It's unclear how realistically 'dictionary' can be null, but the monkey is somehow // managing to get null in here. Presumably the language is changing to a language with @@ -64,7 +63,6 @@ public class AutoCorrection { } int maxFreq = -1; for (final String key : dictionaries.keySet()) { - if (key.equals(Dictionary.TYPE_WHITELIST)) continue; final Dictionary dictionary = dictionaries.get(key); if (null == dictionary) continue; final int tempFreq = dictionary.getFrequency(word); diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 2565dfc66..88d0c09dd 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -42,7 +42,6 @@ public abstract class Dictionary { public static final String TYPE_USER = "user"; // User history dictionary internal to LatinIME. public static final String TYPE_USER_HISTORY = "history"; - public static final String TYPE_WHITELIST ="whitelist"; protected final String mDictType; public Dictionary(final String dictType) { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 1f0bd75cb..e9314084a 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -50,7 +50,6 @@ public class Suggest { private Dictionary mMainDictionary; private ContactsBinaryDictionary mContactsDict; - private WhitelistDictionary mWhiteListDictionary; private final ConcurrentHashMap mDictionaries = new ConcurrentHashMap(); @@ -74,21 +73,11 @@ public class Suggest { mLocale = locale; mMainDictionary = mainDict; addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); - initWhitelistAndAutocorrectAndPool(context, locale); - } - - private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { - mWhiteListDictionary = new WhitelistDictionary(context, locale); - addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary); } private void initAsynchronously(final Context context, final Locale locale, final SuggestInitializationListener listener) { resetMainDict(context, locale, listener); - - // TODO: read the whitelist and init the pool asynchronously too. - // initPool should be done asynchronously now that the pool is thread-safe. - initWhitelistAndAutocorrectAndPool(context, locale); } private static void addOrReplaceDictionary( @@ -225,11 +214,6 @@ public class Suggest { whitelistedWord = suggestionsSet.first().mWord; } - // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" - // but still autocorrected from - in the case the whitelist only capitalizes the word. - // The whitelist should be case-insensitive, so it's not possible to be consistent with - // a boolean flag. Right now this is handled with a slight hack in - // WhitelistDictionary#shouldForciblyAutoCorrectFrom. final boolean allowsToBeAutoCorrected = (null != whitelistedWord && !whitelistedWord.equals(consideredWord)) || AutoCorrection.isNotAWord(mDictionaries, consideredWord, @@ -311,9 +295,10 @@ public class Suggest { // At second character typed, search the unigrams (scores being affected by bigrams) for (final String key : mDictionaries.keySet()) { - // Skip UserUnigramDictionary and WhitelistDictionary to lookup - if (key.equals(Dictionary.TYPE_USER_HISTORY) - || key.equals(Dictionary.TYPE_WHITELIST)) { + // Skip User history dictionary for lookup + // TODO: The user history dictionary should just override getSuggestionsWithSessionId + // to make sure it doesn't return anything and we should remove this test + if (key.equals(Dictionary.TYPE_USER_HISTORY)) { continue; } final Dictionary dictionary = mDictionaries.get(key); diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java deleted file mode 100644 index 14476dcf0..000000000 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2011 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 android.content.Context; -import android.content.res.Resources; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; - -public class WhitelistDictionary extends ExpandableDictionary { - - private static final boolean DBG = LatinImeLogger.sDBG; - private static final String TAG = WhitelistDictionary.class.getSimpleName(); - - private final HashMap> mWhitelistWords = - new HashMap>(); - - // TODO: Conform to the async load contact of ExpandableDictionary - public WhitelistDictionary(final Context context, final Locale locale) { - super(context, Dictionary.TYPE_WHITELIST); - // TODO: Move whitelist dictionary into main dictionary. - final RunInLocale job = new RunInLocale() { - @Override - protected Void job(Resources res) { - initWordlist(res.getStringArray(R.array.wordlist_whitelist)); - return null; - } - }; - job.runInLocale(context.getResources(), locale); - } - - private void initWordlist(String[] wordlist) { - mWhitelistWords.clear(); - final int N = wordlist.length; - if (N % 3 != 0) { - if (DBG) { - Log.d(TAG, "The number of the whitelist is invalid."); - } - return; - } - try { - for (int i = 0; i < N; i += 3) { - final int score = Integer.valueOf(wordlist[i]); - final String before = wordlist[i + 1]; - final String after = wordlist[i + 2]; - if (before != null && after != null) { - mWhitelistWords.put( - before.toLowerCase(), new Pair(score, after)); - addWord(after, null /* shortcut */, score); - } - } - } catch (NumberFormatException e) { - if (DBG) { - Log.d(TAG, "The score of the word is invalid."); - } - } - } - - public String getWhitelistedWord(String before) { - if (before == null) return null; - final String lowerCaseBefore = before.toLowerCase(); - if(mWhitelistWords.containsKey(lowerCaseBefore)) { - if (DBG) { - Log.d(TAG, "--- found whitelistedWord: " + lowerCaseBefore); - } - return mWhitelistWords.get(lowerCaseBefore).second; - } - return null; - } - - @Override - public ArrayList getSuggestions(final WordComposer composer, - final CharSequence prevWord, final ProximityInfo proximityInfo) { - // Whitelist does not supply any suggestions or predictions. - return null; - } - - // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist - // lists that word a should autocorrect to word b, and word c would autocorrect to - // an upper-cased version of a. In this case, the way this return value is used would - // remove the first candidate when the user typed the upper-cased version of A. - // Example : abc -> def and xyz -> Abc - // A user typing Abc would experience it being autocorrected to something else (not - // necessarily def). - // There is no such combination in the whitelist at the time and there probably won't - // ever be - it doesn't make sense. But still. - public boolean shouldForciblyAutoCorrectFrom(CharSequence word) { - if (TextUtils.isEmpty(word)) return false; - final String correction = getWhitelistedWord(word.toString()); - if (TextUtils.isEmpty(correction)) return false; - return !correction.equals(word); - } - - // Leave implementation of getWords and isValidWord to the superclass. - // The words have been added to the ExpandableDictionary with addWord() inside initWordlist. -} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 3bdfe1f27..d05dc021a 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -35,7 +35,6 @@ import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary; import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary; import com.android.inputmethod.latin.UserBinaryDictionary; -import com.android.inputmethod.latin.WhitelistDictionary; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -67,8 +66,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService Collections.synchronizedMap(new TreeMap()); private Map mUserDictionaries = Collections.synchronizedMap(new TreeMap()); - private Map mWhitelistDictionaries = - Collections.synchronizedMap(new TreeMap()); private ContactsBinaryDictionary mContactsDictionary; // The threshold for a candidate to be offered as a suggestion. @@ -366,8 +363,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService final Map oldUserDictionaries = mUserDictionaries; mUserDictionaries = Collections.synchronizedMap(new TreeMap()); - final Map oldWhitelistDictionaries = mWhitelistDictionaries; - mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap()); new Thread("spellchecker_close_dicts") { @Override public void run() { @@ -377,9 +372,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService for (Dictionary dict : oldUserDictionaries.values()) { dict.close(); } - for (Dictionary dict : oldWhitelistDictionaries.values()) { - dict.close(); - } synchronized (mUseContactsLock) { if (null != mContactsDictionary) { // The synchronously loaded contacts dictionary should have been in one @@ -423,12 +415,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService mUserDictionaries.put(localeStr, userDictionary); } dictionaryCollection.addDictionary(userDictionary); - Dictionary whitelistDictionary = mWhitelistDictionaries.get(localeStr); - if (null == whitelistDictionary) { - whitelistDictionary = new WhitelistDictionary(this, locale); - mWhitelistDictionaries.put(localeStr, whitelistDictionary); - } - dictionaryCollection.addDictionary(whitelistDictionary); synchronized (mUseContactsLock) { if (mUseContactsDictionary) { if (null == mContactsDictionary) { From aa27635a8a234b23d6db0957ab398443498eb558 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 17:22:28 +0900 Subject: [PATCH 067/133] Reword a confusing comment Bug: 7005645 Change-Id: Ifd942b3ce242aeeec512e132e1cee31329e994b1 --- .../inputmethod/latin/makedict/BinaryDictInputOutput.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 2c3eee74c..2fe91de89 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -783,10 +783,10 @@ public class BinaryDictInputOutput { // their lower bound and exclude their higher bound so we need to have the first step // start at exactly 1 unit higher than floor(unigramFreq + half a step). // Note : to reconstruct the score, the dictionary reader will need to divide - // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise, and add - // (discretizedFrequency + 0.5) times this value to get the median value of the step, - // which is the best approximation. This is how we get the most precise result with - // only four bits. + // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step, + // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best + // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the + // step pointed by the discretized frequency. final float stepSize = (MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5f + MAX_BIGRAM_FREQUENCY); final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f); From c0a75c8ecbd373c4eaee4f866e4080c0b800470b Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Thu, 16 Aug 2012 18:40:14 +0900 Subject: [PATCH 068/133] add reconstructBigramFrequency Change-Id: Iff20dcb9ca0d6064bb118247887fe24b812c0c61 --- .../latin/UserHistoryDictionary.java | 15 +++++++-------- .../latin/makedict/BinaryDictInputOutput.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index 3bb670c9a..d516e72ad 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -52,14 +52,14 @@ public class UserHistoryDictionary extends ExpandableDictionary { private static final int FREQUENCY_FOR_TYPED = 2; /** Maximum number of pairs. Pruning will start when databases goes above this number. */ - private static int sMaxHistoryBigrams = 10000; + public static final int sMaxHistoryBigrams = 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. */ - private static int sDeleteHistoryBigrams = 1000; + public static final int sDeleteHistoryBigrams = 1000; /** * Database version should increase if the database structure changes @@ -109,12 +109,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { private static DatabaseHelper sOpenHelper = null; - public void setDatabaseMax(int maxHistoryBigram) { - sMaxHistoryBigrams = maxHistoryBigram; - } - - public void setDatabaseDelete(int deleteHistoryBigram) { - sDeleteHistoryBigrams = deleteHistoryBigram; + public String getLocale() { + return mLocale; } public synchronized static UserHistoryDictionary getInstance( @@ -502,9 +498,11 @@ public class UserHistoryDictionary extends ExpandableDictionary { needsToSave(fc, isValid, addLevel0Bigram)) { freq = fc; } else { + // Delete this entry freq = -1; } } else { + // Delete this entry freq = -1; } } @@ -541,6 +539,7 @@ public class UserHistoryDictionary extends ExpandableDictionary { getContentValues(word1, word2, mLocale)); pairId = pairIdLong.intValue(); } + // Eliminate freq == 0 because that word is profanity. if (freq > 0) { if (PROFILE_SAVE_RESTORE) { ++profInsert; diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 2c3eee74c..273ee329d 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -1410,4 +1410,22 @@ public class BinaryDictInputOutput { return false; } } + + /** + * Calculate bigram frequency from compressed value + * + * @see #makeBigramFlags + * + * @param unigramFrequency + * @param bigramFrequency compressed frequency + * @return approximate bigram frequency + */ + public static int reconstructBigramFrequency(final int unigramFrequency, + final int bigramFrequency) { + final float stepSize = (MAX_TERMINAL_FREQUENCY - unigramFrequency) + / (1.5f + MAX_BIGRAM_FREQUENCY); + final float resultFreqFloat = (float)unigramFrequency + + stepSize * (bigramFrequency + 1.0f); + return (int)resultFreqFloat; + } } From 4af100759b462e59b04008be4376e23226703efd Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 17 Aug 2012 20:02:43 +0900 Subject: [PATCH 069/133] (DO NOT MERGE) Remove useless module tags from makefiles Change-Id: If77482da66e80b762cf205369f7d53d069cd8072 --- tools/dicttool/Android.mk | 1 - tools/dicttool/etc/Android.mk | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index df8cb1030..b0b47af00 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -26,7 +26,6 @@ LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \ LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_MODULE := dicttool_aosp LOCAL_JAVA_LIBRARIES := junit -LOCAL_MODULE_TAGS := eng include $(BUILD_HOST_JAVA_LIBRARY) include $(LOCAL_PATH)/etc/Android.mk diff --git a/tools/dicttool/etc/Android.mk b/tools/dicttool/etc/Android.mk index 8952827ab..0c611b7e9 100644 --- a/tools/dicttool/etc/Android.mk +++ b/tools/dicttool/etc/Android.mk @@ -15,6 +15,5 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := eng LOCAL_PREBUILT_EXECUTABLES := dicttool_aosp makedict_aosp include $(BUILD_HOST_PREBUILT) From e46d12927ee2717788248b05f00b751beaf2a02a Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sat, 18 Aug 2012 00:04:33 +0900 Subject: [PATCH 070/133] Fix NPE in LatinIME.addToUserHistoryDictionary() bug: 7007063 Change-Id: Ie3e2f77dcbc597dda0c0df9a970336e5aac0edba --- java/src/com/android/inputmethod/latin/LatinIME.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 884e6db29..c20f3a3a9 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -2001,6 +2001,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) { if (TextUtils.isEmpty(suggestion)) return null; + if (mSuggest == null) return null; // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that From 1964ffb3f43f0ada429d13b41bd22b90a97fa9fc Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 1 Aug 2012 17:26:45 -0700 Subject: [PATCH 071/133] ResearchLogger log RichInputConnection Move many ResearchLogger data collection points to RichInputConnection. By collecting data here, developers do not have to remember to keep the ResearchLog consistent with calls to the RichInputConnection. In addition, some unnecessary log points were removed, and the ResearchLogger is now independent of LatinImeLogger. multi-project change with Ib71f841044ec1072610ab5638a5edfce29b7c05b DO NOT MERGE Bug: 6188932 Change-Id: I9ec7500a5b18bfe1a5a5bb1e5bf21c43351fc59e --- .../android/inputmethod/latin/LatinIME.java | 76 ++----- .../latin/RichInputConnection.java | 86 ++++--- .../inputmethod/research/ResearchLogger.java | 211 ++++++++++-------- 3 files changed, 189 insertions(+), 184 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index c20f3a3a9..a4c82c9f7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -907,13 +907,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(null); + } return; } @@ -935,6 +935,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1055,9 +1058,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1099,12 +1099,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); + ResearchLogger.latinIME_swapSwapperAndSpace(); } mKeyboardSwitcher.updateShiftState(); } @@ -1121,9 +1118,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_doubleSpaceAutoPeriod(); - } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1187,9 +1181,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } } private void handleLanguageSwitchKey() { @@ -1226,6 +1217,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } return; } @@ -1242,9 +1236,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } } // Implementation of {@link KeyboardActionListener}. @@ -1355,9 +1346,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(text); - } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1456,9 +1444,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1478,9 +1463,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1509,9 +1491,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); - } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1530,14 +1509,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1862,10 +1835,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, - autoCorrection.toString()); - } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1891,13 +1860,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); - } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); + } return; } @@ -1924,10 +1893,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, - completionInfo.getText(), x, y); - } return; } @@ -1936,12 +1901,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); - } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); + } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1976,9 +1941,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2046,9 +2008,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2076,9 +2035,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(deleteLength); - } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8b4c17322..41e59e92d 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,7 +55,9 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.beginBatchEdit(); + if (null != mIC) { + mIC.beginBatchEdit(); + } } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -66,7 +68,9 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + if (--mNestLevel == 0 && null != mIC) { + mIC.endBatchEdit(); + } } private void checkBatchEdit() { @@ -79,12 +83,22 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) mIC.finishComposingText(); + if (null != mIC) { + mIC.finishComposingText(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.commitText(text, i); + if (null != mIC) { + mIC.commitText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitText(text, i); + } + } } public int getCursorCapsMode(final int inputType) { @@ -107,37 +121,72 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) mIC.deleteSurroundingText(i, j); + if (null != mIC) { + mIC.deleteSurroundingText(i, j); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_deleteSurroundingText(i, j); + } + } } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.performEditorAction(actionId); + if (null != mIC) { + mIC.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_performEditorAction(actionId); + } + } } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) mIC.sendKeyEvent(keyEvent); + if (null != mIC) { + mIC.sendKeyEvent(keyEvent); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); + } + } } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.setComposingText(text, i); + if (null != mIC) { + mIC.setComposingText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setComposingText(text, i); + } + } } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) mIC.setSelection(from, to); + if (null != mIC) { + mIC.setSelection(from, to); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setSelection(from, to); + } + } } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCorrection(correctionInfo); + if (null != mIC) { + mIC.commitCorrection(correctionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCorrection(correctionInfo); + } + } } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCompletion(completionInfo); + if (null != mIC) { + mIC.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCompletion(completionInfo); + } + } } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -315,9 +364,6 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } @@ -382,13 +428,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } return true; } @@ -409,13 +449,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); - } return true; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index cf6f31a0a..98ed53656 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,12 +37,14 @@ import android.os.IBinder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -846,20 +848,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -914,47 +902,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { - final Object[] values = { - length - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" }; @@ -979,6 +926,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { + // Capture the TextView contents. This will trigger onUpdateSelection(), so we + // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, + // it can tell that it was generated by the logging code, and not by the user, and + // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1048,29 +999,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs, int x, int y) { - final Object[] values = { - index, cs, x, y - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1096,21 +1024,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1121,12 +1034,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { + "LatinIMESwapSwapperAndSpace" }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpace() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1245,6 +1157,109 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { + "RichInputConnectionCommitCompletion", "completionInfo" + }; + public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { + final Object[] values = { + completionInfo + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { + "RichInputConnectionCommitCorrection", "CorrectionInfo" + }; + public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + final String typedWord = correctionInfo.getOldText().toString(); + final String autoCorrection = correctionInfo.getNewText().toString(); + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { + "RichInputConnectionCommitText", "typedWord", "newCursorPosition" + }; + public static void richInputConnection_commitText(final CharSequence typedWord, + final int newCursorPosition) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord, newCursorPosition + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, + values); + researchLogger.onWordComplete(scrubbedWord); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { + "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" + }; + public static void richInputConnection_deleteSurroundingText(final int beforeLength, + final int afterLength) { + final Object[] values = { + beforeLength, afterLength + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { + "RichInputConnectionFinishComposingText" + }; + public static void richInputConnection_finishComposingText() { + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { + "RichInputConnectionPerformEditorAction", "imeActionNext" + }; + public static void richInputConnection_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { + "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" + }; + public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { + final Object[] values = { + keyEvent.getEventTime(), + keyEvent.getAction(), + keyEvent.getKeyCode() + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { + "RichInputConnectionSetComposingText", "text", "newCursorPosition" + }; + public static void richInputConnection_setComposingText(final CharSequence text, + final int newCursorPosition) { + final Object[] values = { + text, newCursorPosition + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { + "RichInputConnectionSetSelection", "from", "to" + }; + public static void richInputConnection_setSelection(final int from, final int to) { + final Object[] values = { + from, to + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; From 221e756fd7d585f0eb75377b851f23cad24ccd7f Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Thu, 2 Aug 2012 20:22:29 -0700 Subject: [PATCH 072/133] ResearchLogging capture full n-gram data DO NOT MERGE - Captures complete motion data for all words in an n-gram. - Also filters n-grams properly; if any word in the n-gram is not in the dictionary, it is not included. - Simplify ResearchLog to not require explicit state - Added LogBuffer class MainLogBuffer class to allow n-gram-level decisions about privacy. - Moved LogUnit out from ResearchLogger multi-project change with Ie2bc79fd7fe6b951b24771e94b8d4ca21989af65 Bug: 6188932 Change-Id: I568c90d4af07e7c759c1e7fc64b716bd8c7b4ae5 --- java/res/values/strings.xml | 3 +- .../android/inputmethod/latin/LatinIME.java | 8 +- .../research/FeedbackActivity.java | 8 +- .../inputmethod/research/LogBuffer.java | 111 ++++ .../android/inputmethod/research/LogUnit.java | 81 +++ .../inputmethod/research/MainLogBuffer.java | 127 +++++ .../inputmethod/research/ResearchLog.java | 259 ++++------ .../research/ResearchLogUploader.java | 37 +- .../inputmethod/research/ResearchLogger.java | 479 +++++++----------- .../inputmethod/research/Statistics.java | 4 +- 10 files changed, 627 insertions(+), 490 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/LogBuffer.java create mode 100644 java/src/com/android/inputmethod/research/LogUnit.java create mode 100644 java/src/com/android/inputmethod/research/MainLogBuffer.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 07b3f31c7..f2468f5a4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -261,7 +261,8 @@ Send feedback - Include last 5 words entered + + Include last %d words entered Enter your feedback here. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a4c82c9f7..bf3501925 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1247,11 +1247,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mLastKeyTime = when; mConnection.beginBatchEdit(); - - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); - } - final KeyboardSwitcher switcher = mKeyboardSwitcher; // The space state depends only on the last character pressed and its own previous // state. Here, we revert the space state to neutral if the key is actually modifying @@ -1333,6 +1328,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord.deactivate(); mEnteredText = null; mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); + } } // Called from PointerTracker through the KeyboardActionListener interface diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java index c9f3b476a..11eae8813 100644 --- a/java/src/com/android/inputmethod/research/FeedbackActivity.java +++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java @@ -18,10 +18,7 @@ package com.android.inputmethod.research; import android.app.Activity; import android.os.Bundle; -import android.text.Editable; -import android.view.View; import android.widget.CheckBox; -import android.widget.EditText; import com.android.inputmethod.latin.R; @@ -31,6 +28,11 @@ public class FeedbackActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.research_feedback_activity); final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout); + final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history); + final CharSequence cs = checkbox.getText(); + final String actualString = String.format(cs.toString(), + ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE); + checkbox.setText(actualString); layout.setActivity(this); } diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java new file mode 100644 index 000000000..65f5f83ae --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogBuffer.java @@ -0,0 +1,111 @@ +/* + * 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.research; + +import java.util.LinkedList; + +/** + * A buffer that holds a fixed number of LogUnits. + * + * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are + * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches + * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to + * stay under the capacity limit. + */ +public class LogBuffer { + protected final LinkedList mLogUnits; + /* package for test */ int mWordCapacity; + // The number of members of mLogUnits that are actual words. + protected int mNumActualWords; + + /** + * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and + * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. + * + * @param wordCapacity maximum number of words + */ + LogBuffer(final int wordCapacity) { + if (wordCapacity <= 0) { + throw new IllegalArgumentException("wordCapacity must be 1 or greater."); + } + mLogUnits = new LinkedList(); + mWordCapacity = wordCapacity; + mNumActualWords = 0; + } + + /** + * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's + * (oldest first) if word capacity is reached. + */ + public void shiftIn(LogUnit newLogUnit) { + if (newLogUnit.getWord() == null) { + // This LogUnit isn't a word, so it doesn't count toward the word-limit. + mLogUnits.add(newLogUnit); + return; + } + if (mNumActualWords == mWordCapacity) { + shiftOutThroughFirstWord(); + } + mLogUnits.add(newLogUnit); + mNumActualWords++; // Must be a word, or we wouldn't be here. + } + + private void shiftOutThroughFirstWord() { + while (!mLogUnits.isEmpty()) { + final LogUnit logUnit = mLogUnits.removeFirst(); + onShiftOut(logUnit); + if (logUnit.hasWord()) { + // Successfully shifted out a word-containing LogUnit and made space for the new + // LogUnit. + mNumActualWords--; + break; + } + } + } + + /** + * Removes all LogUnits from the buffer without calling onShiftOut(). + */ + public void clear() { + mLogUnits.clear(); + mNumActualWords = 0; + } + + /** + * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are + * removed in the order entered. This method is not called when shiftOut is called directly. + * + * Base class does nothing; subclasses may override. + */ + protected void onShiftOut(LogUnit logUnit) { + } + + /** + * Called to deliberately remove the oldest LogUnit. Usually called when draining the + * LogBuffer. + */ + public LogUnit shiftOut() { + if (mLogUnits.isEmpty()) { + return null; + } + final LogUnit logUnit = mLogUnits.removeFirst(); + if (logUnit.hasWord()) { + mNumActualWords--; + } + return logUnit; + } +} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java new file mode 100644 index 000000000..8a80664f5 --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -0,0 +1,81 @@ +/* + * 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.research; + +import java.util.ArrayList; + +/** + * A group of log statements related to each other. + * + * A LogUnit is collection of LogStatements, each of which is generated by at a particular point + * in the code. (There is no LogStatement class; the data is stored across the instance variables + * here.) A single LogUnit's statements can correspond to all the calls made while in the same + * composing region, or all the calls between committing the last composing region, and the first + * character of the next composing region. + * + * Individual statements in a log may be marked as potentially private. If so, then they are only + * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit + * will not violate the user's privacy. Checks for this may include whether other LogUnits have + * been published recently, or whether the LogUnit contains numbers, etc. + */ +/* package */ class LogUnit { + private final ArrayList mKeysList = new ArrayList(); + private final ArrayList mValuesList = new ArrayList(); + private final ArrayList mIsPotentiallyPrivate = new ArrayList(); + private String mWord; + private boolean mContainsDigit; + + public void addLogStatement(final String[] keys, final Object[] values, + final Boolean isPotentiallyPrivate) { + mKeysList.add(keys); + mValuesList.add(values); + mIsPotentiallyPrivate.add(isPotentiallyPrivate); + } + + public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) { + final int size = mKeysList.size(); + for (int i = 0; i < size; i++) { + if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) { + researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + } + } + } + + public void setWord(String word) { + mWord = word; + } + + public String getWord() { + return mWord; + } + + public boolean hasWord() { + return mWord != null; + } + + public void setContainsDigit() { + mContainsDigit = true; + } + + public boolean hasDigit() { + return mContainsDigit; + } + + public boolean isEmpty() { + return mKeysList.isEmpty(); + } +} diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java new file mode 100644 index 000000000..745768d35 --- /dev/null +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -0,0 +1,127 @@ +/* + * 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.research; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Suggest; + +import java.util.Random; + +public class MainLogBuffer extends LogBuffer { + // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. + private static final int N_GRAM_SIZE = 2; + // The number of words between n-grams to omit from the log. + private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18; + + private final ResearchLog mResearchLog; + private Suggest mSuggest; + + // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if + // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc. + // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a + // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe + // n-gram does appear. + /* package for test */ int mMinWordPeriod; + + // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod + // after a sample is taken. + /* package for test */ int mWordsUntilSafeToSample; + + public MainLogBuffer(final ResearchLog researchLog) { + super(N_GRAM_SIZE); + mResearchLog = researchLog; + mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE; + final Random random = new Random(); + mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); + } + + public void setSuggest(Suggest suggest) { + mSuggest = suggest; + } + + @Override + public void shiftIn(final LogUnit newLogUnit) { + super.shiftIn(newLogUnit); + if (newLogUnit.hasWord()) { + if (mWordsUntilSafeToSample > 0) { + mWordsUntilSafeToSample--; + } + } + } + + public void resetWordCounter() { + mWordsUntilSafeToSample = mMinWordPeriod; + } + + /** + * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete + * form and still protect the user's privacy. + * + * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any + * non-character data that is typed between words. The decision about privacy is made based on + * the buffer's entire content. If it is decided that the privacy risks are too great to upload + * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g., + * the screen orientation and other characteristics about the device can be uploaded without + * revealing much about the user. + */ + public boolean isSafeToLog() { + // Check that we are not sampling too frequently. Having sampled recently might disclose + // too much of the user's intended meaning. + if (mWordsUntilSafeToSample > 0) { + return false; + } + if (mSuggest == null || !mSuggest.hasMainDictionary()) { + // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word + // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to + // potentially pose a privacy risk. + return false; + } + // Reload the dictionary in case it has changed (e.g., because the user has changed + // languages). + final Dictionary dictionary = mSuggest.getMainDictionary(); + if (dictionary == null) { + return false; + } + // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the + // complete buffer contents in detail. + final int length = mLogUnits.size(); + for (int i = 0; i < length; i++) { + final LogUnit logUnit = mLogUnits.get(i); + final String word = logUnit.getWord(); + if (word == null) { + // Digits outside words are a privacy threat. + if (logUnit.hasDigit()) { + return false; + } + } else { + // Words not in the dictionary are a privacy threat. + if (!(dictionary.isValidWord(word))) { + return false; + } + } + } + // All checks have passed; this buffer's content can be safely uploaded. + return true; + } + + @Override + protected void onShiftOut(LogUnit logUnit) { + if (mResearchLog != null) { + mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 18bf3c07f..71a6d6a78 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -26,7 +26,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.research.ResearchLogger.LogUnit; import java.io.BufferedWriter; import java.io.File; @@ -37,6 +36,7 @@ import java.io.OutputStreamWriter; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -51,21 +51,22 @@ import java.util.concurrent.TimeUnit; */ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); - private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( - new OutputStreamWriter(new NullOutputStream())); + private static final boolean DEBUG = false; + private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; - final ScheduledExecutorService mExecutor; + /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; private JsonWriter mJsonWriter = NULL_JSON_WRITER; + // true if at least one byte of data has been written out to the log file. This must be + // remembered because JsonWriter requires that calls matching calls to beginObject and + // endObject, as well as beginArray and endArray, and the file is opened lazily, only when + // it is certain that data will be written. Alternatively, the matching call exceptions + // could be caught, but this might suppress other errors. + private boolean mHasWrittenData = false; - private int mLoggingState; - private static final int LOGGING_STATE_UNSTARTED = 0; - private static final int LOGGING_STATE_READY = 1; // don't create file until necessary - private static final int LOGGING_STATE_RUNNING = 2; - private static final int LOGGING_STATE_STOPPING = 3; - private static final int LOGGING_STATE_STOPPED = 4; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; - + private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( + new OutputStreamWriter(new NullOutputStream())); private static class NullOutputStream extends OutputStream { /** {@inheritDoc} */ @Override @@ -84,128 +85,81 @@ public class ResearchLog { } } - public ResearchLog(File outputFile) { - mExecutor = Executors.newSingleThreadScheduledExecutor(); + public ResearchLog(final File outputFile) { if (outputFile == null) { throw new IllegalArgumentException(); } + mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; - mLoggingState = LOGGING_STATE_UNSTARTED; } - public synchronized void start() throws IOException { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_READY; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - break; - } - } - - public synchronized void stop() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - } finally { - boolean success = mFile.setWritable(false, false); - mLoggingState = LOGGING_STATE_STOPPED; - } - return null; + public synchronized void close() { + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.flush(); + mJsonWriter.close(); + mHasWrittenData = false; } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public boolean isAlive() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - return true; - } - return false; - } - - public void waitUntilStopped(final int timeoutInMs) throws InterruptedException { + } catch (Exception e) { + Log.d(TAG, "error when closing ResearchLog:"); + e.printStackTrace(); + } finally { + if (mFile.exists()) { + mFile.setWritable(false, false); + } + } + return null; + } + }); removeAnyScheduledFlush(); mExecutor.shutdown(); - mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); } + private boolean mIsAbortSuccessful; + public synchronized void abort() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - isAbortSuccessful = true; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.close(); - } finally { - isAbortSuccessful = mFile.delete(); - } - return null; + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.close(); + mHasWrittenData = false; } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + } finally { + mIsAbortSuccessful = mFile.delete(); + } + return null; + } + }); + removeAnyScheduledFlush(); + mExecutor.shutdown(); } - private boolean isAbortSuccessful; - public boolean isAbortSuccessful() { - return isAbortSuccessful; + public boolean blockingAbort() throws InterruptedException { + abort(); + mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + return mIsAbortSuccessful; + } + + public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException { + mExecutor.awaitTermination(delay, timeUnit); } /* package */ synchronized void flush() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + removeAnyScheduledFlush(); + mExecutor.submit(mFlushCallable); } - private Callable mFlushCallable = new Callable() { + private final Callable mFlushCallable = new Callable() { @Override public Object call() throws Exception { - if (mLoggingState == LOGGING_STATE_RUNNING) { - mJsonWriter.flush(); - } + mJsonWriter.flush(); return null; } }; @@ -224,56 +178,40 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } - public synchronized void publishPublicEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - logUnit.publishPublicEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public synchronized void publishAllEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - logUnit.publishAllEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: + public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) { + try { + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + logUnit.publishTo(ResearchLog.this, isIncludingPrivateData); + scheduleFlush(); + return null; + } + }); + } catch (RejectedExecutionException e) { + // TODO: Add code to record loss of data, and report. } } private static final String CURRENT_TIME_KEY = "_ct"; private static final String UPTIME_KEY = "_ut"; private static final String EVENT_TYPE_KEY = "_ty"; + void outputEvent(final String[] keys, final Object[] values) { - // not thread safe. + // Not thread safe. + if (keys.length == 0) { + return; + } + if (DEBUG) { + if (keys.length != values.length + 1) { + Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); + } + } try { if (mJsonWriter == NULL_JSON_WRITER) { mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.setLenient(true); mJsonWriter.beginArray(); + mHasWrittenData = true; } mJsonWriter.beginObject(); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); @@ -283,8 +221,8 @@ public class ResearchLog { for (int i = 0; i < length; i++) { mJsonWriter.name(keys[i + 1]); Object value = values[i]; - if (value instanceof String) { - mJsonWriter.value((String) value); + if (value instanceof CharSequence) { + mJsonWriter.value(value.toString()); } else if (value instanceof Number) { mJsonWriter.value((Number) value); } else if (value instanceof Boolean) { @@ -331,14 +269,11 @@ public class ResearchLog { SuggestedWords words = (SuggestedWords) value; mJsonWriter.beginObject(); mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect") - .value(words.mWillAutoCorrect); + mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions") - .value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction") - .value(words.mIsPrediction); + .value(words.mIsPunctuationSuggestions); + mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); + mJsonWriter.name("isPrediction").value(words.mIsPrediction); mJsonWriter.name("words"); mJsonWriter.beginArray(); final int size = words.size(); @@ -363,8 +298,8 @@ public class ResearchLog { try { mJsonWriter.close(); } catch (IllegalStateException e1) { - // assume that this is just the json not being terminated properly. - // ignore + // Assume that this is just the json not being terminated properly. + // Ignore } catch (IOException e1) { e1.printStackTrace(); } finally { diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java index 3b1213009..9904a1de2 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -27,7 +27,6 @@ import android.os.BatteryManager; import android.util.Log; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.R.string; import java.io.BufferedReader; import java.io.File; @@ -48,6 +47,7 @@ public final class ResearchLogUploader { private static final String TAG = ResearchLogUploader.class.getSimpleName(); private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; private final boolean mCanUpload; private final Context mContext; @@ -55,8 +55,6 @@ public final class ResearchLogUploader { private final URL mUrl; private final ScheduledExecutorService mExecutor; - private Runnable doUploadRunnable = new UploadRunnable(null, false); - public ResearchLogUploader(final Context context, final File filesDir) { mContext = context; mFilesDir = filesDir; @@ -93,11 +91,15 @@ public final class ResearchLogUploader { public void start() { if (mCanUpload) { - Log.d(TAG, "scheduling regular uploading"); - mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS, - UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } else { - Log.d(TAG, "no permission to upload"); + mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, + null /* callback */, false /* forceUpload */), + UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } + } + + public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); } } @@ -106,7 +108,8 @@ public final class ResearchLogUploader { // another upload happening right now, as it may have missed the latest changes. // TODO: Reschedule regular upload tests starting from now. if (mCanUpload) { - mExecutor.submit(new UploadRunnable(callback, true)); + mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, + true /* forceUpload */)); } } @@ -130,19 +133,33 @@ public final class ResearchLogUploader { } class UploadRunnable implements Runnable { + private final ResearchLog mLogToWaitFor; private final Callback mCallback; private final boolean mForceUpload; - public UploadRunnable(final Callback callback, final boolean forceUpload) { + public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, + final boolean forceUpload) { + mLogToWaitFor = logToWaitFor; mCallback = callback; mForceUpload = forceUpload; } @Override public void run() { + if (mLogToWaitFor != null) { + waitFor(mLogToWaitFor); + } doUpload(); } + private void waitFor(final ResearchLog researchLog) { + try { + researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private void doUpload() { if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { return; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b5bdc1958..3cad2d099 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -67,11 +67,8 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.Locale; import java.util.UUID; @@ -97,24 +94,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); private static final boolean IS_SHOWING_INDICATOR = true; private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false; + public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; - private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user private static final ResearchLogger sInstance = new ResearchLogger(); // to write to a different filename, e.g., for testing, set mFile before calling start() /* package */ File mFilesDir; /* package */ String mUUIDString; /* package */ ResearchLog mMainResearchLog; - // The mIntentionalResearchLog records all events for the session, private or not (excepting - // passwords). It is written to permanent storage only if the user explicitly commands - // the system to do so. - /* package */ ResearchLog mIntentionalResearchLog; - // LogUnits are queued here and released only when the user requests the intentional log. - private List mIntentionalResearchLogQueue = new ArrayList(); + /* package */ ResearchLog mFeedbackLog; + /* package */ MainLogBuffer mMainLogBuffer; + /* package */ LogBuffer mFeedbackLogBuffer; private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -139,9 +133,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; private final Statistics mStatistics; - private ResearchLogUploader mResearchLogUploader; + private LogUnit mCurrentLogUnit = new LogUnit(); + private ResearchLogger() { mStatistics = Statistics.getInstance(); } @@ -264,6 +259,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } + private void setLoggingAllowed(boolean enableLogging) { + if (mPrefs == null) { + return; + } + Editor e = mPrefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); + e.apply(); + sIsLogging = enableLogging; + } + private File createLogFile(File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(FILENAME_PREFIX).append('-'); @@ -310,97 +315,58 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); return; } - try { - if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mMainResearchLog.start(); - if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mIntentionalResearchLog.start(); - } catch (IOException e) { - Log.w(TAG, "Could not start ResearchLogger."); + if (mMainLogBuffer == null) { + mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); + mMainLogBuffer = new MainLogBuffer(mMainResearchLog); + mMainLogBuffer.setSuggest(mSuggest); + } + if (mFeedbackLogBuffer == null) { + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); + // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold + // the feedback LogUnit itself. + mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); } } /* package */ void stop() { logStatistics(); - publishLogUnit(mCurrentLogUnit, true); - mCurrentLogUnit = new LogUnit(); + commitCurrentLogUnit(); - if (mMainResearchLog != null) { - mMainResearchLog.stop(); + if (mMainLogBuffer != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */); + mMainResearchLog.close(); + mMainLogBuffer = null; } - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.stop(); + if (mFeedbackLogBuffer != null) { + mFeedbackLog.close(); + mFeedbackLogBuffer = null; } } - private static final String[] EVENTKEYS_STATISTICS = { - "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", - "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", - "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" - }; - private static void logStatistics() { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - final Object[] values = { - statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, - statistics.mSpaceCount, statistics.mDeleteKeyCount, - statistics.mWordCount, statistics.mIsEmptyUponStarting, - statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), - statistics.mBeforeDeleteKeyCounter.getAverageTime(), - statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), - statistics.mAfterDeleteKeyCounter.getAverageTime() - }; - researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); - } - - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; - } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); - sIsLogging = enableLogging; - } - public boolean abort() { boolean didAbortMainLog = false; - if (mMainResearchLog != null) { - mMainResearchLog.abort(); + if (mMainLogBuffer != null) { + mMainLogBuffer.clear(); try { - mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortMainLog = mMainResearchLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - if (mMainResearchLog.isAbortSuccessful()) { - didAbortMainLog = true; - } - mMainResearchLog = null; + mMainLogBuffer = null; } - boolean didAbortIntentionalLog = false; - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.abort(); + boolean didAbortFeedbackLog = false; + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.clear(); try { - mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortFeedbackLog = mFeedbackLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - if (mIntentionalResearchLog.isAbortSuccessful()) { - didAbortIntentionalLog = true; - } - mIntentionalResearchLog = null; - } - return didAbortMainLog && didAbortIntentionalLog; - } - - /* package */ void flush() { - if (mMainResearchLog != null) { - mMainResearchLog.flush(); + mFeedbackLogBuffer = null; } + return didAbortMainLog && didAbortFeedbackLog; } private void restart() { @@ -504,79 +470,39 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } - private ResearchLog mFeedbackLog; - private List mFeedbackQueue; - private ResearchLog mSavedMainResearchLog; - private ResearchLog mSavedIntentionalResearchLog; - private List mSavedIntentionalResearchLogQueue; - - private void saveLogsForFeedback() { - mFeedbackLog = mIntentionalResearchLog; - if (mIntentionalResearchLogQueue != null) { - mFeedbackQueue = new ArrayList(mIntentionalResearchLogQueue); - } else { - mFeedbackQueue = null; - } - mSavedMainResearchLog = mMainResearchLog; - mSavedIntentionalResearchLog = mIntentionalResearchLog; - mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue; - - mMainResearchLog = null; - mIntentionalResearchLog = null; - mIntentionalResearchLogQueue = new ArrayList(); - } - - private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; + private static final String[] EVENTKEYS_FEEDBACK = { + "UserTimestamp", "contents" + }; public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (includeHistory && mFeedbackLog != null) { - try { - LogUnit headerLogUnit = new LogUnit(); - headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false); - mFeedbackLog.publishAllEvents(headerLogUnit); - for (LogUnit logUnit : mFeedbackQueue) { - mFeedbackLog.publishAllEvents(logUnit); - } - userFeedback(mFeedbackLog, feedbackContents); - mFeedbackLog.stop(); - try { - mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - mIntentionalResearchLog.start(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - mIntentionalResearchLogQueue.clear(); - } - mResearchLogUploader.uploadNow(null); - } else { - // create a separate ResearchLog just for feedback - final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir)); - try { - feedbackLog.start(); - userFeedback(feedbackLog, feedbackContents); - feedbackLog.stop(); - feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - mResearchLogUploader.uploadNow(null); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if (mFeedbackLogBuffer == null) { + return; } + if (!includeHistory) { + mFeedbackLogBuffer.clear(); + } + commitCurrentLogUnit(); + final LogUnit feedbackLogUnit = new LogUnit(); + final Object[] values = { + feedbackContents + }; + feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values, + false /* isPotentiallyPrivate */); + mFeedbackLogBuffer.shiftIn(feedbackLogUnit); + publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); + mFeedbackLog.close(); + mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; - mMainResearchLog = mSavedMainResearchLog; - mIntentionalResearchLog = mSavedIntentionalResearchLog; - mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue; } public void initSuggest(Suggest suggest) { mSuggest = suggest; + if (mMainLogBuffer != null) { + mMainLogBuffer.setSuggest(mSuggest); + } } private void setIsPasswordView(boolean isPasswordView) { @@ -584,7 +510,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging; + return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; } public void requestIndicatorRedraw() { @@ -631,13 +557,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; private static final Object[] EVENTKEYS_NULLVALUES = {}; - private LogUnit mCurrentLogUnit = new LogUnit(); - /** * Buffer a research log event, flagging it as privacy-sensitive. * @@ -653,10 +574,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, true); + mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */); } } + private void setCurrentLogUnitContainsDigitFlag() { + mCurrentLogUnit.setContainsDigit(); + } + /** * Buffer a research log event, flaggint it as not privacy-sensitive. * @@ -672,140 +597,54 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void enqueueEvent(final String[] keys, final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, false); + mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */); } } - // Used to track how often words are logged. Too-frequent logging can leak - // semantics, disclosing private data. - /* package for test */ static class LoggingFrequencyState { - private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; - private int mWordsRemainingToSkip; - private final int mFrequency; - - /** - * Tracks how often words may be uploaded. - * - * @param frequency 1=Every word, 2=Every other word, etc. - */ - public LoggingFrequencyState(int frequency) { - mFrequency = frequency; - mWordsRemainingToSkip = mFrequency; - } - - public void onWordLogged() { - mWordsRemainingToSkip = mFrequency; - } - - public void onWordNotLogged() { - if (mWordsRemainingToSkip > 1) { - mWordsRemainingToSkip--; - } - } - - public boolean isSafeToLog() { - return mWordsRemainingToSkip <= 1; - } - } - - /* package for test */ LoggingFrequencyState mLoggingFrequencyState = - new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); - - /* package for test */ boolean isPrivacyThreat(String word) { - // Current checks: - // - Word not in dictionary - // - Word contains numbers - // - Privacy-safe word not logged recently - if (TextUtils.isEmpty(word)) { - return false; - } - if (!mLoggingFrequencyState.isSafeToLog()) { - return true; - } - final int length = word.length(); - boolean hasLetter = false; - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(word, i); - if (Character.isDigit(codePoint)) { - return true; - } - if (Character.isLetter(codePoint)) { - hasLetter = true; - break; // Word may contain digits, but will only be allowed if in the dictionary. - } - } - if (hasLetter) { - if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { - mDictionary = mSuggest.getMainDictionary(); - } - if (mDictionary == null) { - // Can't access dictionary. Assume privacy threat. - return true; - } - return !(mDictionary.isValidWord(word)); - } - // No letters, no numbers. Punctuation, space, or something else. - return false; - } - - private void onWordComplete(String word) { - if (isPrivacyThreat(word)) { - publishLogUnit(mCurrentLogUnit, true); - mLoggingFrequencyState.onWordNotLogged(); - } else { - publishLogUnit(mCurrentLogUnit, false); - mLoggingFrequencyState.onWordLogged(); - } - mCurrentLogUnit = new LogUnit(); - mStatistics.recordWordEntered(); - } - - private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { - if (!isAllowedToLog()) { - return; - } - if (mMainResearchLog == null) { - return; - } - if (isPrivacySensitive) { - mMainResearchLog.publishPublicEvents(logUnit); - } else { - mMainResearchLog.publishAllEvents(logUnit); - } - mIntentionalResearchLogQueue.add(logUnit); - } - - /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) { - publishLogUnit(mCurrentLogUnit, isPrivacySensitive); - } - - static class LogUnit { - private final List mKeysList = new ArrayList(); - private final List mValuesList = new ArrayList(); - private final List mIsPotentiallyPrivate = new ArrayList(); - - private void addLogAtom(final String[] keys, final Object[] values, - final Boolean isPotentiallyPrivate) { - mKeysList.add(keys); - mValuesList.add(values); - mIsPotentiallyPrivate.add(isPotentiallyPrivate); - } - - public void publishPublicEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - if (!mIsPotentiallyPrivate.get(i)) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + /* package for test */ void commitCurrentLogUnit() { + if (!mCurrentLogUnit.isEmpty()) { + if (mMainLogBuffer != null) { + mMainLogBuffer.shiftIn(mCurrentLogUnit); + if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, + true /* isIncludingPrivateData */); + mMainLogBuffer.resetWordCounter(); } } + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); + } + mCurrentLogUnit = new LogUnit(); + Log.d(TAG, "commitCurrentLogUnit"); } + } - public void publishAllEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + /* package for test */ void publishLogBuffer(final LogBuffer logBuffer, + final ResearchLog researchLog, final boolean isIncludingPrivateData) { + LogUnit logUnit; + while ((logUnit = logBuffer.shiftOut()) != null) { + researchLog.publish(logUnit, isIncludingPrivateData); + } + } + + private boolean hasOnlyLetters(final String word) { + final int length = word.length(); + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = word.codePointAt(i); + if (!Character.isLetter(codePoint)) { + return false; } } + return true; + } + + private void onWordComplete(final String word) { + Log.d(TAG, "onWordComplete: " + word); + if (word != null && word.length() > 0 && hasOnlyLetters(word)) { + mCurrentLogUnit.setWord(word); + mStatistics.recordWordEntered(); + } + commitCurrentLogUnit(); } private static int scrubDigitFromCodePoint(int codePoint) { @@ -858,12 +697,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return WORD_REPLACEMENT_STRING; } - // Special methods related to startup, shutdown, logging itself - - private static final String[] EVENTKEYS_INTENTIONAL_LOG = { - "IntentionalLog" - }; - private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" @@ -871,9 +704,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs) { final ResearchLogger researchLogger = getInstance(); - if (researchLogger.mInFeedbackDialog) { - researchLogger.saveLogsForFeedback(); - } researchLogger.start(); if (editorInfo != null) { final Context context = researchLogger.mInputMethodService; @@ -905,16 +735,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; - private void userFeedback(ResearchLog researchLog, String feedbackContents) { - // this method is special; it directs the feedbackContents to a particular researchLog - final LogUnit logUnit = new LogUnit(); - final Object[] values = { - feedbackContents - }; - logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false); - researchLog.publishAllEvents(logUnit); - } - // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { @@ -949,12 +769,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEOnCodeInput", "code", "x", "y" }; public static void latinIME_onCodeInput(final int code, final int x, final int y) { + final long time = SystemClock.uptimeMillis(); + final ResearchLogger researchLogger = getInstance(); final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } + researchLogger.mStatistics.recordChar(code, time); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { @@ -1019,9 +843,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); - // Play it safe. Remove privacy-sensitive events. - researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); - researchLogger.mCurrentLogUnit = new LogUnit(); + researchLogger.commitCurrentLogUnit(); getInstance().stop(); } } @@ -1086,7 +908,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)) }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } } private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { @@ -1224,10 +1050,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); } + // Disabled for privacy-protection reasons. Because this event comes after + // richInputConnection_commitText, which is the event used to separate LogUnits, the + // data in this event can be associated with the next LogUnit, revealing information + // about the current word even if it was supposed to be suppressed. The occurrance of + // autocorrection can be determined by examining the difference between the text strings in + // the last call to richInputConnection_setComposingText before + // richInputConnection_commitText, so it's not a data loss. + // TODO: Figure out how to log this event without loss of privacy. + /* private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { - "RichInputConnectionCommitCorrection", "CorrectionInfo" + "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection" }; + */ public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + /* final String typedWord = correctionInfo.getOldText().toString(); final String autoCorrection = correctionInfo.getNewText().toString(); final Object[] values = { @@ -1236,6 +1073,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + */ } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { @@ -1261,7 +1099,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { beforeLength, afterLength }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { @@ -1291,7 +1130,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang keyEvent.getAction(), keyEvent.getKeyCode() }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, + values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { @@ -1299,10 +1139,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; public static void richInputConnection_setComposingText(final CharSequence text, final int newCursorPosition) { + if (text == null) { + throw new RuntimeException("setComposingText is null"); + } final Object[] values = { text, newCursorPosition }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, + values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { @@ -1312,7 +1156,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { from, to }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, + values); } private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { @@ -1347,4 +1192,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void userTimestamp() { getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); } + + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index 4a2cd079c..eab465aa2 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -66,8 +66,8 @@ public class Statistics { // To account for the interruptions when the user's attention is directed elsewhere, times // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds + public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds // The last time that a tap was performed private long mLastTapTime; From 7cec911fde16775c28db3dbb16b064fc7ce69b55 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 30 Jul 2012 21:55:42 -0700 Subject: [PATCH 073/133] ResearchLogger to track simple statistics DO NOT MERGE Bug: 6188932 Change-Id: I4667f8f60aa356c73f925c298318520f71e144c5 --- .../inputmethod/research/ResearchLogger.java | 57 ++++++- .../inputmethod/research/Statistics.java | 146 ++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 java/src/com/android/inputmethod/research/Statistics.java diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 98ed53656..b5bdc1958 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -34,6 +34,7 @@ import android.graphics.Paint.Style; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.IBinder; +import android.os.SystemClock; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; @@ -137,10 +138,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private Dictionary mDictionary; private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; + private final Statistics mStatistics; private ResearchLogUploader mResearchLogUploader; private ResearchLogger() { + mStatistics = Statistics.getInstance(); } public static ResearchLogger getInstance() { @@ -270,10 +273,35 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } + private void checkForEmptyEditor() { + if (mInputMethodService == null) { + return; + } + final InputConnection ic = mInputMethodService.getCurrentInputConnection(); + if (ic == null) { + return; + } + final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(textBefore)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + final CharSequence textAfter = ic.getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(textAfter)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + if (textBefore != null && textAfter != null) { + mStatistics.setIsEmptyUponStarting(true); + } + } + private void start() { maybeShowSplashScreen(); updateSuspendedState(); requestIndicatorRedraw(); + mStatistics.reset(); + checkForEmptyEditor(); if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; @@ -297,6 +325,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /* package */ void stop() { + logStatistics(); + publishLogUnit(mCurrentLogUnit, true); + mCurrentLogUnit = new LogUnit(); + if (mMainResearchLog != null) { mMainResearchLog.stop(); } @@ -305,6 +337,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } + private void setLoggingAllowed(boolean enableLogging) { if (mPrefs == null) { return; @@ -705,6 +757,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mLoggingFrequencyState.onWordLogged(); } mCurrentLogUnit = new LogUnit(); + mStatistics.recordWordEntered(); } private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { @@ -899,7 +952,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java new file mode 100644 index 000000000..4a2cd079c --- /dev/null +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -0,0 +1,146 @@ +/* + * 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.research; + +import com.android.inputmethod.keyboard.Keyboard; + +public class Statistics { + // Number of characters entered during a typing session + int mCharCount; + // Number of letter characters entered during a typing session + int mLetterCount; + // Number of number characters entered + int mNumberCount; + // Number of space characters entered + int mSpaceCount; + // Number of delete operations entered (taps on the backspace key) + int mDeleteKeyCount; + // Number of words entered during a session. + int mWordCount; + // Whether the text field was empty upon editing + boolean mIsEmptyUponStarting; + boolean mIsEmptinessStateKnown; + + // Timers to count average time to enter a key, first press a delete key, + // between delete keys, and then to return typing after a delete key. + final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); + final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); + + static class AverageTimeCounter { + int mCount; + int mTotalTime; + + public void reset() { + mCount = 0; + mTotalTime = 0; + } + + public void add(long deltaTime) { + mCount++; + mTotalTime += deltaTime; + } + + public int getAverageTime() { + if (mCount == 0) { + return 0; + } + return mTotalTime / mCount; + } + } + + // To account for the interruptions when the user's attention is directed elsewhere, times + // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. + public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds + + // The last time that a tap was performed + private long mLastTapTime; + // The type of the last keypress (delete key or not) + boolean mIsLastKeyDeleteKey; + + private static final Statistics sInstance = new Statistics(); + + public static Statistics getInstance() { + return sInstance; + } + + private Statistics() { + reset(); + } + + public void reset() { + mCharCount = 0; + mLetterCount = 0; + mNumberCount = 0; + mSpaceCount = 0; + mDeleteKeyCount = 0; + mWordCount = 0; + mIsEmptyUponStarting = true; + mIsEmptinessStateKnown = false; + mKeyCounter.reset(); + mBeforeDeleteKeyCounter.reset(); + mDuringRepeatedDeleteKeysCounter.reset(); + mAfterDeleteKeyCounter.reset(); + + mLastTapTime = 0; + mIsLastKeyDeleteKey = false; + } + + public void recordChar(int codePoint, long time) { + final long delta = time - mLastTapTime; + if (codePoint == Keyboard.CODE_DELETE) { + mDeleteKeyCount++; + if (delta < MIN_DELETION_INTERMISSION) { + if (mIsLastKeyDeleteKey) { + mDuringRepeatedDeleteKeysCounter.add(delta); + } else { + mBeforeDeleteKeyCounter.add(delta); + } + } + mIsLastKeyDeleteKey = true; + } else { + mCharCount++; + if (Character.isDigit(codePoint)) { + mNumberCount++; + } + if (Character.isLetter(codePoint)) { + mLetterCount++; + } + if (Character.isSpaceChar(codePoint)) { + mSpaceCount++; + } + if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { + mAfterDeleteKeyCounter.add(delta); + } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { + mKeyCounter.add(delta); + } + mIsLastKeyDeleteKey = false; + } + mLastTapTime = time; + } + + public void recordWordEntered() { + mWordCount++; + } + + public void setIsEmptyUponStarting(final boolean isEmpty) { + mIsEmptyUponStarting = isEmpty; + mIsEmptinessStateKnown = true; + } +} From baf1f0bd616ca1fc3b53ad832012f498b3601afb Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Sat, 4 Aug 2012 23:26:35 -0700 Subject: [PATCH 074/133] ResearchLogging upload via service DO NOT MERGE Upload using an intent service rather than just a thread. More robust in case the keyboard is closed and the upload hasn't finished yet. multi-project commit with I40db74fb780e01364609339764e150f0291d3f9b Bug: 6188932 Change-Id: Ie980d38a713d15c01083d41bd73f0602ec75dd16 --- java/res/values/strings.xml | 4 + .../research/BootBroadcastReceiver.java | 33 +++ .../research/ResearchLogUploader.java | 240 ------------------ .../inputmethod/research/ResearchLogger.java | 46 +++- .../inputmethod/research/UploaderService.java | 191 ++++++++++++++ 5 files changed, 268 insertions(+), 246 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/BootBroadcastReceiver.java delete mode 100644 java/src/com/android/inputmethod/research/ResearchLogUploader.java create mode 100644 java/src/com/android/inputmethod/research/UploaderService.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index f2468f5a4..35cbcf3c4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -289,6 +289,10 @@ Send usage info + + + Research Uploader Service + Input languages diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java new file mode 100644 index 000000000..5124a35a6 --- /dev/null +++ b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java @@ -0,0 +1,33 @@ +/* + * 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.research; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * Arrange for the uploading service to be run on regular intervals. + */ +public final class BootBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + ResearchLogger.scheduleUploadingService(context); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java deleted file mode 100644 index 9904a1de2..000000000 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.research; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.BatteryManager; -import android.util.Log; - -import com.android.inputmethod.latin.R; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -public final class ResearchLogUploader { - private static final String TAG = ResearchLogUploader.class.getSimpleName(); - private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min - private static final int BUF_SIZE = 1024 * 8; - protected static final int TIMEOUT_IN_MS = 1000 * 4; - - private final boolean mCanUpload; - private final Context mContext; - private final File mFilesDir; - private final URL mUrl; - private final ScheduledExecutorService mExecutor; - - public ResearchLogUploader(final Context context, final File filesDir) { - mContext = context; - mFilesDir = filesDir; - final PackageManager packageManager = context.getPackageManager(); - final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED; - if (!hasPermission) { - mCanUpload = false; - mUrl = null; - mExecutor = null; - return; - } - URL tempUrl = null; - boolean canUpload = false; - ScheduledExecutorService executor = null; - try { - final String urlString = context.getString(R.string.research_logger_upload_url); - if (urlString == null || urlString.equals("")) { - return; - } - tempUrl = new URL(urlString); - canUpload = true; - executor = Executors.newSingleThreadScheduledExecutor(); - } catch (MalformedURLException e) { - tempUrl = null; - e.printStackTrace(); - return; - } finally { - mCanUpload = canUpload; - mUrl = tempUrl; - mExecutor = executor; - } - } - - public void start() { - if (mCanUpload) { - mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, - null /* callback */, false /* forceUpload */), - UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } - } - - public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { - if (mCanUpload) { - mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); - } - } - - public void uploadNow(final Callback callback) { - // Perform an immediate upload. Note that this should happen even if there is - // another upload happening right now, as it may have missed the latest changes. - // TODO: Reschedule regular upload tests starting from now. - if (mCanUpload) { - mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, - true /* forceUpload */)); - } - } - - public interface Callback { - public void onUploadCompleted(final boolean success); - } - - private boolean isExternallyPowered() { - final Intent intent = mContext.registerReceiver(null, new IntentFilter( - Intent.ACTION_BATTERY_CHANGED)); - final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - return pluggedState == BatteryManager.BATTERY_PLUGGED_AC - || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; - } - - private boolean hasWifiConnection() { - final ConnectivityManager manager = - (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - return wifiInfo.isConnected(); - } - - class UploadRunnable implements Runnable { - private final ResearchLog mLogToWaitFor; - private final Callback mCallback; - private final boolean mForceUpload; - - public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, - final boolean forceUpload) { - mLogToWaitFor = logToWaitFor; - mCallback = callback; - mForceUpload = forceUpload; - } - - @Override - public void run() { - if (mLogToWaitFor != null) { - waitFor(mLogToWaitFor); - } - doUpload(); - } - - private void waitFor(final ResearchLog researchLog) { - try { - researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private void doUpload() { - if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { - return; - } - if (mFilesDir == null) { - return; - } - final File[] files = mFilesDir.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) - && !pathname.canWrite(); - } - }); - boolean success = true; - if (files.length == 0) { - success = false; - } - for (final File file : files) { - if (!uploadFile(file)) { - success = false; - } - } - if (mCallback != null) { - mCallback.onUploadCompleted(success); - } - } - - private boolean uploadFile(File file) { - Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); - boolean success = false; - final int contentLength = (int) file.length(); - HttpURLConnection connection = null; - InputStream fileIs = null; - try { - fileIs = new FileInputStream(file); - connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("PUT"); - connection.setDoOutput(true); - connection.setFixedLengthStreamingMode(contentLength); - final OutputStream os = connection.getOutputStream(); - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = fileIs.read(buf)) != -1) { - os.write(buf, 0, numBytesRead); - } - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.d(TAG, "upload failed: " + connection.getResponseCode()); - InputStream netIs = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(netIs)); - String line; - while ((line = reader.readLine()) != null) { - Log.d(TAG, "| " + reader.readLine()); - } - reader.close(); - return success; - } - file.delete(); - success = true; - Log.d(TAG, "upload successful"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (fileIs != null) { - try { - fileIs.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (connection != null) { - connection.disconnect(); - } - } - return success; - } - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index bd62ca35e..814a12673 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -18,11 +18,14 @@ package com.android.inputmethod.research; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Dialog; +import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; @@ -133,7 +136,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; private final Statistics mStatistics; - private ResearchLogUploader mResearchLogUploader; + + private Intent mUploadIntent; + private PendingIntent mUploadPendingIntent; private LogUnit mCurrentLogUnit = new LogUnit(); @@ -176,11 +181,34 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } } - mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); - mResearchLogUploader.start(); mKeyboardSwitcher = keyboardSwitcher; mInputMethodService = ims; mPrefs = prefs; + mUploadIntent = new Intent(mInputMethodService, UploaderService.class); + mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0); + + if (ProductionFlag.IS_EXPERIMENTAL) { + scheduleUploadingService(mInputMethodService); + } + } + + /** + * Arrange for the UploaderService to be run on a regular basis. + * + * Any existing scheduled invocation of UploaderService is removed and rescheduled. This may + * cause problems if this method is called often and frequent updates are required, but since + * the user will likely be sleeping at some point, if the interval is less that the expected + * sleep duration and this method is not called during that time, the service should be invoked + * at some point. + */ + public static void scheduleUploadingService(Context context) { + final Intent intent = new Intent(context, UploaderService.class); + final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); + final AlarmManager manager = + (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + manager.cancel(pendingIntent); + manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, + UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent); } private void cleanupLoggingDir(final File dir, final long time) { @@ -257,6 +285,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Editor e = mPrefs.edit(); e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true); e.apply(); + restart(); } private void setLoggingAllowed(boolean enableLogging) { @@ -479,10 +508,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mFeedbackLogBuffer == null) { return; } - if (!includeHistory) { + if (includeHistory) { + commitCurrentLogUnit(); + } else { mFeedbackLogBuffer.clear(); } - commitCurrentLogUnit(); final LogUnit feedbackLogUnit = new LogUnit(); final Object[] values = { feedbackContents @@ -492,10 +522,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mFeedbackLogBuffer.shiftIn(feedbackLogUnit); publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); mFeedbackLog.close(); - mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); + uploadNow(); mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } + public void uploadNow() { + mInputMethodService.startService(mUploadIntent); + } + public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; } diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java new file mode 100644 index 000000000..7a5749096 --- /dev/null +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -0,0 +1,191 @@ +/* + * 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.research; + +import android.Manifest; +import android.app.AlarmManager; +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.BatteryManager; +import android.os.Bundle; +import android.util.Log; + +import com.android.inputmethod.latin.R; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public final class UploaderService extends IntentService { + private static final String TAG = UploaderService.class.getSimpleName(); + public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; + private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() + + ".extra.UPLOAD_UNCONDITIONALLY"; + private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; + + private boolean mCanUpload; + private File mFilesDir; + private URL mUrl; + + public UploaderService() { + super("Research Uploader Service"); + } + + @Override + public void onCreate() { + super.onCreate(); + + mCanUpload = false; + mFilesDir = null; + mUrl = null; + + final PackageManager packageManager = getPackageManager(); + final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, + getPackageName()) == PackageManager.PERMISSION_GRANTED; + if (!hasPermission) { + return; + } + + try { + final String urlString = getString(R.string.research_logger_upload_url); + if (urlString == null || urlString.equals("")) { + return; + } + mFilesDir = getFilesDir(); + mUrl = new URL(urlString); + mCanUpload = true; + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + @Override + protected void onHandleIntent(Intent intent) { + if (!mCanUpload) { + return; + } + boolean isUploadingUnconditionally = false; + Bundle bundle = intent.getExtras(); + if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { + isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); + } + doUpload(isUploadingUnconditionally); + } + + private boolean isExternallyPowered() { + final Intent intent = registerReceiver(null, new IntentFilter( + Intent.ACTION_BATTERY_CHANGED)); + final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return pluggedState == BatteryManager.BATTERY_PLUGGED_AC + || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; + } + + private boolean hasWifiConnection() { + final ConnectivityManager manager = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } + + private void doUpload(final boolean isUploadingUnconditionally) { + if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) { + return; + } + if (mFilesDir == null) { + return; + } + final File[] files = mFilesDir.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) + && !pathname.canWrite(); + } + }); + boolean success = true; + if (files.length == 0) { + success = false; + } + for (final File file : files) { + if (!uploadFile(file)) { + success = false; + } + } + } + + private boolean uploadFile(File file) { + Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); + boolean success = false; + final int contentLength = (int) file.length(); + HttpURLConnection connection = null; + InputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(file); + connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(contentLength); + final OutputStream os = connection.getOutputStream(); + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = fileInputStream.read(buf)) != -1) { + os.write(buf, 0, numBytesRead); + } + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.d(TAG, "upload failed: " + connection.getResponseCode()); + InputStream netInputStream = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream)); + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "| " + reader.readLine()); + } + reader.close(); + return success; + } + file.delete(); + success = true; + Log.d(TAG, "upload successful"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fileInputStream != null) { + try { + fileInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (connection != null) { + connection.disconnect(); + } + } + return success; + } +} From c58f5d904bceab9b33840a7de4e3de4323b2126b Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 6 Aug 2012 18:29:51 -0700 Subject: [PATCH 075/133] ResearchLogging log when preferences change DO NOT MERGE Bug: 6188932 Change-Id: Ia7ee65e7f3be10d031a065aaa6c3d18610c7dff0 --- .../inputmethod/research/ResearchLogger.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 3cad2d099..bd62ca35e 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -407,6 +407,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang abort(); } requestIndicatorRedraw(); + mPrefs = prefs; + prefsChanged(prefs); } public void presentResearchDialog(final LatinIME latinIME) { @@ -735,6 +737,17 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; + private static final String[] EVENTKEYS_PREFS_CHANGED = { + "PrefsChanged", "prefs" + }; + public static void prefsChanged(final SharedPreferences prefs) { + final ResearchLogger researchLogger = getInstance(); + final Object[] values = { + prefs + }; + researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values); + } + // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { From 522e12660a8541a5e98b4737d55e40bd6c49ee00 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sun, 19 Aug 2012 01:26:05 +0900 Subject: [PATCH 076/133] Cosmetic fixes Change-Id: I5460252af534d4455ba22273a06ac956bc904831 --- .../inputmethod/keyboard/Keyboard.java | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 919850095..489cc37ea 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -283,9 +283,10 @@ public class Keyboard { public void load(String[] data) { final int dataLength = data.length; if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) { - if (LatinImeLogger.sDBG) + if (LatinImeLogger.sDBG) { throw new RuntimeException( "the size of touch position correction data is invalid"); + } return; } @@ -324,7 +325,7 @@ public class Keyboard { public boolean isValid() { return mEnabled && mXs != null && mYs != null && mRadii != null - && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0; + && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0; } } @@ -870,10 +871,12 @@ public class Keyboard { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); try { - if (a.hasValue(R.styleable.Keyboard_horizontalGap)) + if (a.hasValue(R.styleable.Keyboard_horizontalGap)) { throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap"); - if (a.hasValue(R.styleable.Keyboard_verticalGap)) + } + if (a.hasValue(R.styleable.Keyboard_verticalGap)) { throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap"); + } return new Row(mResources, mParams, parser, mCurrentY); } finally { a.recycle(); @@ -921,7 +924,9 @@ public class Keyboard { throws XmlPullParserException, IOException { if (skip) { XmlParseUtils.checkEndTag(TAG_KEY, parser); - if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY); + if (DEBUG) { + startEndTag("<%s /> skipped", TAG_KEY); + } } else { final Key key = new Key(mResources, mParams, row, parser); if (DEBUG) { @@ -1099,9 +1104,9 @@ public class Keyboard { private boolean parseCaseCondition(XmlPullParser parser) { final KeyboardId id = mParams.mId; - if (id == null) + if (id == null) { return true; - + } final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Case); try { @@ -1205,9 +1210,9 @@ public class Keyboard { // If does not have "index" attribute, that means this is wild-card for // the attribute. final TypedValue v = a.peekValue(index); - if (v == null) + if (v == null) { return true; - + } if (isIntegerValue(v)) { return intValue == a.getInt(index, 0); } else if (isStringValue(v)) { @@ -1218,8 +1223,9 @@ public class Keyboard { private static boolean stringArrayContains(String[] array, String value) { for (final String elem : array) { - if (elem.equals(value)) + if (elem.equals(value)) { return true; + } } return false; } @@ -1242,16 +1248,18 @@ public class Keyboard { TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); try { - if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) + if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) { throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE + "/> needs styleName attribute", parser); + } if (DEBUG) { startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE, - keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName), - skip ? " skipped" : ""); + keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName), + skip ? " skipped" : ""); } - if (!skip) + if (!skip) { mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); + } } finally { keyStyleAttr.recycle(); keyAttrs.recycle(); @@ -1272,8 +1280,9 @@ public class Keyboard { } private void endRow(Row row) { - if (mCurrentRow == null) + if (mCurrentRow == null) { throw new InflateException("orphan end row tag"); + } if (mRightEdgeKey != null) { mRightEdgeKey.markAsRightEdge(mParams); mRightEdgeKey = null; @@ -1309,8 +1318,9 @@ public class Keyboard { public static float getDimensionOrFraction(TypedArray a, int index, int base, float defValue) { final TypedValue value = a.peekValue(index); - if (value == null) + if (value == null) { return defValue; + } if (isFractionValue(value)) { return a.getFraction(index, base, base, defValue); } else if (isDimensionValue(value)) { @@ -1321,8 +1331,9 @@ public class Keyboard { public static int getEnumValue(TypedArray a, int index, int defValue) { final TypedValue value = a.peekValue(index); - if (value == null) + if (value == null) { return defValue; + } if (isIntegerValue(value)) { return a.getInt(index, defValue); } From 6c63f712655efe7e2d72cb2a0af9a0d8fdf0c730 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 20 Aug 2012 11:24:16 +0900 Subject: [PATCH 077/133] Add standard view constructors of PrevewPlacerView Change-Id: I00d7ae4d2330804c342a5e51cf8c0df6ed8a85d6 --- .../inputmethod/keyboard/KeyboardView.java | 22 ++++++++++--------- .../keyboard/internal/PreviewPlacerView.java | 10 ++++++++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index fcf97b99c..ccbb081b4 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -362,9 +362,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - mKeyDrawParams = new KeyDrawParams(a); mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams); + mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0); if (mKeyPreviewLayoutId == 0) { mShowKeyPreviewPopup = false; @@ -373,11 +373,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { R.styleable.KeyboardView_verticalCorrection, 0); mMoreKeysLayout = a.getResourceId(R.styleable.KeyboardView_moreKeysLayout, 0); mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0); - mPreviewPlacerView = new PreviewPlacerView(context, a); a.recycle(); - mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; - + mPreviewPlacerView = new PreviewPlacerView(context, attrs); mPaint.setAntiAlias(true); } @@ -462,12 +460,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { if (bufferNeedsUpdates || mOffscreenBuffer == null) { if (maybeAllocateOffscreenBuffer()) { mInvalidateAllKeys = true; - // TODO: Stop using the offscreen canvas even when in software rendering - if (mOffscreenCanvas != null) { - mOffscreenCanvas.setBitmap(mOffscreenBuffer); - } else { - mOffscreenCanvas = new Canvas(mOffscreenBuffer); - } + maybeCreateOffscreenCanvas(); } onDrawKeyboard(mOffscreenCanvas); } @@ -496,6 +489,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } + private void maybeCreateOffscreenCanvas() { + // TODO: Stop using the offscreen canvas even when in software rendering + if (mOffscreenCanvas != null) { + mOffscreenCanvas.setBitmap(mOffscreenBuffer); + } else { + mOffscreenCanvas = new Canvas(mOffscreenBuffer); + } + } + private void onDrawKeyboard(final Canvas canvas) { if (mKeyboard == null) return; diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index d0fecf060..9e9c9e29a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -23,6 +23,7 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.os.Message; import android.text.TextUtils; +import android.util.AttributeSet; import android.util.SparseArray; import android.widget.RelativeLayout; @@ -89,10 +90,16 @@ public class PreviewPlacerView extends RelativeLayout { } } - public PreviewPlacerView(Context context, TypedArray keyboardViewAttr) { + public PreviewPlacerView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.keyboardViewStyle); + } + + public PreviewPlacerView(Context context, AttributeSet attrs, int defStyle) { super(context); setWillNotDraw(false); + final TypedArray keyboardViewAttr = context.obtainStyledAttributes( + attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize( R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0); mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor( @@ -117,6 +124,7 @@ public class PreviewPlacerView extends RelativeLayout { R.styleable.KeyboardView_gesturePreviewTrailColor, 0); final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize( R.styleable.KeyboardView_gesturePreviewTrailWidth, 0); + keyboardViewAttr.recycle(); mGesturePaint = new Paint(); mGesturePaint.setAntiAlias(true); From fb087a95f9dd1898d5a81ffe8642f36db44efc0c Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 20 Aug 2012 13:51:27 +0900 Subject: [PATCH 078/133] Fix capitalization, part 1 The case this fixes is typing "Ah.\n" in gmail for example Bug: 6950087 Change-Id: Ia4bf3a57dde49b9c9d47d315e9f85b7ffc7ee6e3 --- java/src/com/android/inputmethod/latin/LatinIME.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index c20f3a3a9..a7896ad52 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1696,6 +1696,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)primaryCode, x, y); + mHandler.postUpdateShiftState(); return didAutoCorrect; } From 13822d2b056543de5a54b5ed338ca2cc250d8287 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 17 Aug 2012 16:19:03 +0900 Subject: [PATCH 079/133] Hack to skip reading an outdated binary file. Bug: 7005813 Change-Id: Ie0d8d4b2d5eb147838ca23bdd5ec1cecd4f01151 --- .../latin/BinaryDictionaryGetter.java | 49 +++++++++++++++++-- .../latin/makedict/BinaryDictInputOutput.java | 23 ++++++--- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 063243e1b..dd11aaa37 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; + import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; @@ -23,6 +25,7 @@ import android.content.res.AssetFileDescriptor; import android.util.Log; import java.io.File; +import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; @@ -51,6 +54,9 @@ class BinaryDictionaryGetter { private static final String MAIN_DICTIONARY_CATEGORY = "main"; public static final String ID_CATEGORY_SEPARATOR = ":"; + // The key considered to read the version attribute in a dictionary file. + private static String VERSION_KEY = "version"; + // Prevents this from being instantiated private BinaryDictionaryGetter() {} @@ -336,6 +342,42 @@ class BinaryDictionaryGetter { return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]); } + // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason + // for this is, since those do not include whitelist entries, the new code with an old version + // of the dictionary would lose whitelist functionality. + private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) { + // Only for English - other languages didn't have a whitelist, hence this + // ad-hock ## HACK ## + if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true; + + try { + // Read the version of the file + final RandomAccessFile raf = new RandomAccessFile(f, "r"); + final int magic = raf.readInt(); + if (magic != BinaryDictInputOutput.VERSION_2_MAGIC_NUMBER) { + return false; + } + final int formatVersion = raf.readInt(); + final int headerSize = raf.readInt(); + final HashMap options = new HashMap(); + BinaryDictInputOutput.populateOptionsFromFile(raf, headerSize, options); + final String version = options.get(VERSION_KEY); + if (null == version) { + // No version in the options : the format is unexpected + return false; + } + // Version 18 is the first one to include the whitelist + // Obviously this is a big ## HACK ## + return Integer.parseInt(version) >= 18; + } catch (java.io.FileNotFoundException e) { + return false; + } catch (java.io.IOException e) { + return false; + } catch (NumberFormatException e) { + return false; + } + } + /** * Returns a list of file addresses for a given locale, trying relevant methods in order. * @@ -366,14 +408,15 @@ class BinaryDictionaryGetter { // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = getWordListIdFromFileName(f.getName()); - if (isMainWordListId(wordListId)) { + final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f); + if (canUse && isMainWordListId(wordListId)) { foundMainDict = true; } if (!dictPackSettings.isWordListActive(wordListId)) continue; - if (f.canRead()) { + if (canUse) { fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); } else { - Log.e(TAG, "Found a cached dictionary file but cannot read it"); + Log.e(TAG, "Found a cached dictionary file but cannot read or use it"); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 7f042335a..b23b7db34 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -124,7 +124,7 @@ public class BinaryDictInputOutput { */ private static final int VERSION_1_MAGIC_NUMBER = 0x78B1; - private static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; + public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; private static final int MINIMUM_SUPPORTED_VERSION = 1; private static final int MAXIMUM_SUPPORTED_VERSION = 2; private static final int NOT_A_VERSION_NUMBER = -1; @@ -1327,6 +1327,21 @@ public class BinaryDictInputOutput { return NOT_A_VERSION_NUMBER; } + /** + * Reads options from a file and populate a map with their contents. + * + * The file is read at the current file pointer, so the caller must take care the pointer + * is in the right place before calling this. + */ + public static void populateOptionsFromFile(final RandomAccessFile source, final long headerSize, + final HashMap options) throws IOException { + while (source.getFilePointer() < headerSize) { + final String key = CharEncoding.readString(source); + final String value = CharEncoding.readString(source); + options.put(key, value); + } + } + /** * Reads a random access file and returns the memory representation of the dictionary. * @@ -1358,11 +1373,7 @@ public class BinaryDictInputOutput { } else { headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16) + (source.readUnsignedByte() << 8) + source.readUnsignedByte(); - while (source.getFilePointer() < headerSize) { - final String key = CharEncoding.readString(source); - final String value = CharEncoding.readString(source); - options.put(key, value); - } + populateOptionsFromFile(source, headerSize, options); source.seek(headerSize); } From 5d9e573fbfe01139b264a5ea22f5a9af6a9def02 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Mon, 20 Aug 2012 14:25:14 +0900 Subject: [PATCH 080/133] Remove the whitelist resources Bug: 6906525 Change-Id: I1524b9e4c56d28a701d444aa2afbfdcecf0da571 --- java/res/values-en/whitelist.xml | 411 ------------------------------- java/res/values/whitelist.xml | 29 --- 2 files changed, 440 deletions(-) delete mode 100644 java/res/values-en/whitelist.xml delete mode 100644 java/res/values/whitelist.xml diff --git a/java/res/values-en/whitelist.xml b/java/res/values-en/whitelist.xml deleted file mode 100644 index 262017916..000000000 --- a/java/res/values-en/whitelist.xml +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - - 255 - ill - I\'ll - - - - 255 - acomodate - accommodate - - 255 - aint - ain\'t - - 255 - alot - a lot - - 255 - andteh - and the - - 255 - arent - aren\'t - - 255 - bot - not - - 255 - bern - been - - 255 - bot - not - - 255 - bur - but - - 255 - cam - can - - 255 - cant - can\'t - - 255 - dame - same - - 255 - didint - didn\'t - - 255 - dormer - former - - 255 - dud - did - - 255 - fay - day - - 255 - fife - five - - 255 - foo - for - - 255 - fora - for a - - 255 - galled - called - - 255 - goo - too - - 255 - hed - he\'d - - 255 - hel - he\'ll - - 255 - heres - here\'s - - 255 - hew - new - - 255 - hoe - how - - 255 - hoes - how\'s - - 255 - howd - how\'d - - 255 - howll - how\'ll - - 255 - hows - how\'s - - 255 - howve - how\'ve - - 255 - hum - him - - 255 - i - I - - 255 - ifs - its - - 255 - il - I\'ll - - 255 - im - I\'m - - 255 - inteh - in the - - 255 - itd - it\'d - - 255 - itsa - it\'s a - - 255 - lets - let\'s - - 255 - maam - ma\'am - - 255 - manu - many - - 255 - mare - made - - 255 - mew - new - - 255 - mire - more - - 255 - moat - most - - 255 - mot - not - - 255 - mote - note - - 255 - motes - notes - - 255 - mow - now - - 255 - namer - named - - 255 - nave - have - - 255 - nee - new - - 255 - nigh - high - - 255 - nit - not - - 255 - oft - off - - 255 - os - is - - 255 - pater - later - - 255 - rook - took - - 255 - shel - she\'ll - - 255 - shouldent - shouldn\'t - - 255 - sill - will - - 255 - sown - down - - 255 - thatd - that\'d - - 255 - tine - time - - 255 - thong - thing - - 255 - tome - time - - - 255 - uf - if - - - 255 - un - in - - - 255 - UnitedStates - United States - - 255 - unitedstates - United States - - 255 - visavis - vis-a-vis - - 255 - wierd - weird - - 255 - wel - we\'ll - - 255 - wer - we\'re - - 255 - whatd - what\'d - - 255 - whatm - what\'m - - 255 - whatre - what\'re - - 255 - whats - what\'s - - 255 - whens - when\'s - - 255 - whered - where\'d - - 255 - wherell - where\'ll - - 255 - wheres - where\'s - - 255 - wholl - who\'ll - - 255 - whove - who\'ve - - 255 - whyd - why\'d - - 255 - whyll - why\'ll - - 255 - whys - why\'s - - 255 - whyve - why\'ve - - 255 - wont - won\'t - - 255 - yall - y\'all - - 255 - youd - you\'d - - - diff --git a/java/res/values/whitelist.xml b/java/res/values/whitelist.xml deleted file mode 100644 index d4ecbfaa4..000000000 --- a/java/res/values/whitelist.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - From 8ce351a8275f0ad73cdd642e8b46a430b072e8ef Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 20 Aug 2012 14:37:16 +0900 Subject: [PATCH 081/133] Make some usages of SparseArray thread-safe This is a follow up of I39d9bf1a7c272eb16d6ed4698f52457579b40f10. Change-Id: Ic3288e491175f9a85679a21064093f9db5a65466 --- .../inputmethod/latin/BinaryDictionary.java | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 726c44cc1..2d9e0ce2c 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -64,18 +64,21 @@ public class BinaryDictionary extends Dictionary { private final SparseArray mDicTraverseSessions = new SparseArray(); + + // TODO: There should be a way to remove used DicTraverseSession objects from + // {@code mDicTraverseSessions}. private DicTraverseSession getTraverseSession(int traverseSessionId) { - DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); - if (traverseSession == null) { - synchronized(mDicTraverseSessions) { + synchronized(mDicTraverseSessions) { + DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); + if (traverseSession == null) { traverseSession = mDicTraverseSessions.get(traverseSessionId); if (traverseSession == null) { traverseSession = new DicTraverseSession(mLocale, mNativeDict); mDicTraverseSessions.put(traverseSessionId, traverseSession); } } + return traverseSession; } - return traverseSession; } /** @@ -209,18 +212,20 @@ public class BinaryDictionary extends Dictionary { } @Override - public synchronized void close() { - for (int i = 0; i < mDicTraverseSessions.size(); ++i) { - final int key = mDicTraverseSessions.keyAt(i); - final DicTraverseSession traverseSession = mDicTraverseSessions.get(key); - if (traverseSession != null) { - traverseSession.close(); + public void close() { + synchronized (mDicTraverseSessions) { + final int sessionsSize = mDicTraverseSessions.size(); + for (int index = 0; index < sessionsSize; ++index) { + final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index); + if (traverseSession != null) { + traverseSession.close(); + } } } closeInternal(); } - private void closeInternal() { + private synchronized void closeInternal() { if (mNativeDict != 0) { closeNative(mNativeDict); mNativeDict = 0; From 978c0fc87897c7d08be124871c50017d395af950 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 20 Aug 2012 14:27:45 +0900 Subject: [PATCH 082/133] Cleaup gesture preview drawing code a bit Change-Id: Ie75d497ba88805d945d6039ecad09ed0e8e5e4d3 --- .../keyboard/internal/PreviewPlacerView.java | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 9e9c9e29a..59a92d625 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -50,6 +50,9 @@ public class PreviewPlacerView extends RelativeLayout { private final SparseArray mPointers = new SparseArray(); private String mGestureFloatingPreviewText; + private int mLastPointerX; + private int mLastPointerY; + private boolean mDrawsGesturePreviewTrail; private boolean mDrawsGestureFloatingPreviewText; @@ -154,31 +157,30 @@ public class PreviewPlacerView extends RelativeLayout { public void invalidatePointer(PointerTracker tracker) { synchronized (mPointers) { mPointers.put(tracker.mPointerId, tracker); - // TODO: Should narrow the invalidate region. - invalidate(); } + mLastPointerX = tracker.getLastX(); + mLastPointerY = tracker.getLastY(); + // TODO: Should narrow the invalidate region. + invalidate(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - synchronized (mPointers) { - canvas.translate(mXOrigin, mYOrigin); - final int trackerCount = mPointers.size(); - boolean hasDrawnFloatingPreviewText = false; - for (int index = 0; index < trackerCount; index++) { - final PointerTracker tracker = mPointers.valueAt(index); - if (mDrawsGesturePreviewTrail) { + canvas.translate(mXOrigin, mYOrigin); + if (mDrawsGesturePreviewTrail) { + synchronized (mPointers) { + final int trackerCount = mPointers.size(); + for (int index = 0; index < trackerCount; index++) { + final PointerTracker tracker = mPointers.valueAt(index); tracker.drawGestureTrail(canvas, mGesturePaint); } - // TODO: Figure out more cleaner way to draw gesture preview text. - if (mDrawsGestureFloatingPreviewText && !hasDrawnFloatingPreviewText) { - drawGestureFloatingPreviewText(canvas, tracker, mGestureFloatingPreviewText); - hasDrawnFloatingPreviewText = true; - } } - canvas.translate(-mXOrigin, -mYOrigin); } + if (mDrawsGestureFloatingPreviewText) { + drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText); + } + canvas.translate(-mXOrigin, -mYOrigin); } public void setGestureFloatingPreviewText(String gestureFloatingPreviewText) { @@ -194,15 +196,16 @@ public class PreviewPlacerView extends RelativeLayout { mDrawingHandler.cancelAllMessages(); } - private void drawGestureFloatingPreviewText(Canvas canvas, PointerTracker tracker, - String gestureFloatingPreviewText) { + private void drawGestureFloatingPreviewText(Canvas canvas, String gestureFloatingPreviewText) { if (TextUtils.isEmpty(gestureFloatingPreviewText)) { return; } final Paint paint = mTextPaint; - final int lastX = tracker.getLastX(); - final int lastY = tracker.getLastY(); + // TODO: Figure out how we should deal with the floating preview text with multiple moving + // fingers. + final int lastX = mLastPointerX; + final int lastY = mLastPointerY; final int textSize = (int)paint.getTextSize(); final int canvasWidth = canvas.getWidth(); From 96da6335820ce4e1558ff2afe90738cba62a8afc Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 20 Aug 2012 10:17:29 -0700 Subject: [PATCH 083/133] Revert "ResearchLogging upload via service" This reverts commit baf1f0bd616ca1fc3b53ad832012f498b3601afb Change-Id: I7d959b9b626c8fb69a57788e243752952258fa80 --- java/res/values/strings.xml | 4 - .../research/BootBroadcastReceiver.java | 33 --- .../research/ResearchLogUploader.java | 240 ++++++++++++++++++ .../inputmethod/research/ResearchLogger.java | 46 +--- .../inputmethod/research/UploaderService.java | 191 -------------- 5 files changed, 246 insertions(+), 268 deletions(-) delete mode 100644 java/src/com/android/inputmethod/research/BootBroadcastReceiver.java create mode 100644 java/src/com/android/inputmethod/research/ResearchLogUploader.java delete mode 100644 java/src/com/android/inputmethod/research/UploaderService.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 35cbcf3c4..f2468f5a4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -289,10 +289,6 @@ Send usage info - - - Research Uploader Service - Input languages diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java deleted file mode 100644 index 5124a35a6..000000000 --- a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.research; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -/** - * Arrange for the uploading service to be run on regular intervals. - */ -public final class BootBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { - ResearchLogger.scheduleUploadingService(context); - } - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java new file mode 100644 index 000000000..9904a1de2 --- /dev/null +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -0,0 +1,240 @@ +/* + * 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.research; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.BatteryManager; +import android.util.Log; + +import com.android.inputmethod.latin.R; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public final class ResearchLogUploader { + private static final String TAG = ResearchLogUploader.class.getSimpleName(); + private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min + private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; + + private final boolean mCanUpload; + private final Context mContext; + private final File mFilesDir; + private final URL mUrl; + private final ScheduledExecutorService mExecutor; + + public ResearchLogUploader(final Context context, final File filesDir) { + mContext = context; + mFilesDir = filesDir; + final PackageManager packageManager = context.getPackageManager(); + final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, + context.getPackageName()) == PackageManager.PERMISSION_GRANTED; + if (!hasPermission) { + mCanUpload = false; + mUrl = null; + mExecutor = null; + return; + } + URL tempUrl = null; + boolean canUpload = false; + ScheduledExecutorService executor = null; + try { + final String urlString = context.getString(R.string.research_logger_upload_url); + if (urlString == null || urlString.equals("")) { + return; + } + tempUrl = new URL(urlString); + canUpload = true; + executor = Executors.newSingleThreadScheduledExecutor(); + } catch (MalformedURLException e) { + tempUrl = null; + e.printStackTrace(); + return; + } finally { + mCanUpload = canUpload; + mUrl = tempUrl; + mExecutor = executor; + } + } + + public void start() { + if (mCanUpload) { + mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, + null /* callback */, false /* forceUpload */), + UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } + } + + public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); + } + } + + public void uploadNow(final Callback callback) { + // Perform an immediate upload. Note that this should happen even if there is + // another upload happening right now, as it may have missed the latest changes. + // TODO: Reschedule regular upload tests starting from now. + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, + true /* forceUpload */)); + } + } + + public interface Callback { + public void onUploadCompleted(final boolean success); + } + + private boolean isExternallyPowered() { + final Intent intent = mContext.registerReceiver(null, new IntentFilter( + Intent.ACTION_BATTERY_CHANGED)); + final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return pluggedState == BatteryManager.BATTERY_PLUGGED_AC + || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; + } + + private boolean hasWifiConnection() { + final ConnectivityManager manager = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } + + class UploadRunnable implements Runnable { + private final ResearchLog mLogToWaitFor; + private final Callback mCallback; + private final boolean mForceUpload; + + public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, + final boolean forceUpload) { + mLogToWaitFor = logToWaitFor; + mCallback = callback; + mForceUpload = forceUpload; + } + + @Override + public void run() { + if (mLogToWaitFor != null) { + waitFor(mLogToWaitFor); + } + doUpload(); + } + + private void waitFor(final ResearchLog researchLog) { + try { + researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void doUpload() { + if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { + return; + } + if (mFilesDir == null) { + return; + } + final File[] files = mFilesDir.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) + && !pathname.canWrite(); + } + }); + boolean success = true; + if (files.length == 0) { + success = false; + } + for (final File file : files) { + if (!uploadFile(file)) { + success = false; + } + } + if (mCallback != null) { + mCallback.onUploadCompleted(success); + } + } + + private boolean uploadFile(File file) { + Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); + boolean success = false; + final int contentLength = (int) file.length(); + HttpURLConnection connection = null; + InputStream fileIs = null; + try { + fileIs = new FileInputStream(file); + connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(contentLength); + final OutputStream os = connection.getOutputStream(); + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = fileIs.read(buf)) != -1) { + os.write(buf, 0, numBytesRead); + } + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.d(TAG, "upload failed: " + connection.getResponseCode()); + InputStream netIs = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(netIs)); + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "| " + reader.readLine()); + } + reader.close(); + return success; + } + file.delete(); + success = true; + Log.d(TAG, "upload successful"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fileIs != null) { + try { + fileIs.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (connection != null) { + connection.disconnect(); + } + } + return success; + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 814a12673..bd62ca35e 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -18,14 +18,11 @@ package com.android.inputmethod.research; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Dialog; -import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; -import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; @@ -136,9 +133,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; private final Statistics mStatistics; - - private Intent mUploadIntent; - private PendingIntent mUploadPendingIntent; + private ResearchLogUploader mResearchLogUploader; private LogUnit mCurrentLogUnit = new LogUnit(); @@ -181,34 +176,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } } + mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); + mResearchLogUploader.start(); mKeyboardSwitcher = keyboardSwitcher; mInputMethodService = ims; mPrefs = prefs; - mUploadIntent = new Intent(mInputMethodService, UploaderService.class); - mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0); - - if (ProductionFlag.IS_EXPERIMENTAL) { - scheduleUploadingService(mInputMethodService); - } - } - - /** - * Arrange for the UploaderService to be run on a regular basis. - * - * Any existing scheduled invocation of UploaderService is removed and rescheduled. This may - * cause problems if this method is called often and frequent updates are required, but since - * the user will likely be sleeping at some point, if the interval is less that the expected - * sleep duration and this method is not called during that time, the service should be invoked - * at some point. - */ - public static void scheduleUploadingService(Context context) { - final Intent intent = new Intent(context, UploaderService.class); - final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); - final AlarmManager manager = - (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - manager.cancel(pendingIntent); - manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, - UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent); } private void cleanupLoggingDir(final File dir, final long time) { @@ -285,7 +257,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Editor e = mPrefs.edit(); e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true); e.apply(); - restart(); } private void setLoggingAllowed(boolean enableLogging) { @@ -508,11 +479,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mFeedbackLogBuffer == null) { return; } - if (includeHistory) { - commitCurrentLogUnit(); - } else { + if (!includeHistory) { mFeedbackLogBuffer.clear(); } + commitCurrentLogUnit(); final LogUnit feedbackLogUnit = new LogUnit(); final Object[] values = { feedbackContents @@ -522,14 +492,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mFeedbackLogBuffer.shiftIn(feedbackLogUnit); publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); mFeedbackLog.close(); - uploadNow(); + mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } - public void uploadNow() { - mInputMethodService.startService(mUploadIntent); - } - public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; } diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java deleted file mode 100644 index 7a5749096..000000000 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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.research; - -import android.Manifest; -import android.app.AlarmManager; -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.BatteryManager; -import android.os.Bundle; -import android.util.Log; - -import com.android.inputmethod.latin.R; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; - -public final class UploaderService extends IntentService { - private static final String TAG = UploaderService.class.getSimpleName(); - public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; - private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() - + ".extra.UPLOAD_UNCONDITIONALLY"; - private static final int BUF_SIZE = 1024 * 8; - protected static final int TIMEOUT_IN_MS = 1000 * 4; - - private boolean mCanUpload; - private File mFilesDir; - private URL mUrl; - - public UploaderService() { - super("Research Uploader Service"); - } - - @Override - public void onCreate() { - super.onCreate(); - - mCanUpload = false; - mFilesDir = null; - mUrl = null; - - final PackageManager packageManager = getPackageManager(); - final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, - getPackageName()) == PackageManager.PERMISSION_GRANTED; - if (!hasPermission) { - return; - } - - try { - final String urlString = getString(R.string.research_logger_upload_url); - if (urlString == null || urlString.equals("")) { - return; - } - mFilesDir = getFilesDir(); - mUrl = new URL(urlString); - mCanUpload = true; - } catch (MalformedURLException e) { - e.printStackTrace(); - } - } - - @Override - protected void onHandleIntent(Intent intent) { - if (!mCanUpload) { - return; - } - boolean isUploadingUnconditionally = false; - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { - isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); - } - doUpload(isUploadingUnconditionally); - } - - private boolean isExternallyPowered() { - final Intent intent = registerReceiver(null, new IntentFilter( - Intent.ACTION_BATTERY_CHANGED)); - final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - return pluggedState == BatteryManager.BATTERY_PLUGGED_AC - || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; - } - - private boolean hasWifiConnection() { - final ConnectivityManager manager = - (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - return wifiInfo.isConnected(); - } - - private void doUpload(final boolean isUploadingUnconditionally) { - if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) { - return; - } - if (mFilesDir == null) { - return; - } - final File[] files = mFilesDir.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) - && !pathname.canWrite(); - } - }); - boolean success = true; - if (files.length == 0) { - success = false; - } - for (final File file : files) { - if (!uploadFile(file)) { - success = false; - } - } - } - - private boolean uploadFile(File file) { - Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); - boolean success = false; - final int contentLength = (int) file.length(); - HttpURLConnection connection = null; - InputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(file); - connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("PUT"); - connection.setDoOutput(true); - connection.setFixedLengthStreamingMode(contentLength); - final OutputStream os = connection.getOutputStream(); - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = fileInputStream.read(buf)) != -1) { - os.write(buf, 0, numBytesRead); - } - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.d(TAG, "upload failed: " + connection.getResponseCode()); - InputStream netInputStream = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream)); - String line; - while ((line = reader.readLine()) != null) { - Log.d(TAG, "| " + reader.readLine()); - } - reader.close(); - return success; - } - file.delete(); - success = true; - Log.d(TAG, "upload successful"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (connection != null) { - connection.disconnect(); - } - } - return success; - } -} From fc74a01abe3a741afb691dcf5b8fb0a9595b8dba Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 20 Aug 2012 10:17:31 -0700 Subject: [PATCH 084/133] Revert "ResearchLogging log when preferences change" This reverts commit c58f5d904bceab9b33840a7de4e3de4323b2126b Change-Id: Ifab54ecc8b7898fe0af6446d397efd6cdb28f670 --- .../inputmethod/research/ResearchLogger.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index bd62ca35e..3cad2d099 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -407,8 +407,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang abort(); } requestIndicatorRedraw(); - mPrefs = prefs; - prefsChanged(prefs); } public void presentResearchDialog(final LatinIME latinIME) { @@ -737,17 +735,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; - private static final String[] EVENTKEYS_PREFS_CHANGED = { - "PrefsChanged", "prefs" - }; - public static void prefsChanged(final SharedPreferences prefs) { - final ResearchLogger researchLogger = getInstance(); - final Object[] values = { - prefs - }; - researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values); - } - // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { From 3d3590874926ff92009ed0b4f114c6e5a8ee7394 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 20 Aug 2012 10:17:40 -0700 Subject: [PATCH 085/133] Revert "ResearchLogging capture full n-gram data" This reverts commit 221e756fd7d585f0eb75377b851f23cad24ccd7f Change-Id: Iefc4e4e27ddc925d4a4634627b0467bd4ee2a66e --- java/res/values/strings.xml | 3 +- .../android/inputmethod/latin/LatinIME.java | 8 +- .../research/FeedbackActivity.java | 8 +- .../inputmethod/research/LogBuffer.java | 111 ---- .../android/inputmethod/research/LogUnit.java | 81 --- .../inputmethod/research/MainLogBuffer.java | 127 ----- .../inputmethod/research/ResearchLog.java | 263 ++++++---- .../research/ResearchLogUploader.java | 37 +- .../inputmethod/research/ResearchLogger.java | 479 +++++++++++------- .../inputmethod/research/Statistics.java | 4 +- 10 files changed, 492 insertions(+), 629 deletions(-) delete mode 100644 java/src/com/android/inputmethod/research/LogBuffer.java delete mode 100644 java/src/com/android/inputmethod/research/LogUnit.java delete mode 100644 java/src/com/android/inputmethod/research/MainLogBuffer.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index f2468f5a4..07b3f31c7 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -261,8 +261,7 @@ Send feedback - - Include last %d words entered + Include last 5 words entered Enter your feedback here. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index bf3501925..a4c82c9f7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1247,6 +1247,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mLastKeyTime = when; mConnection.beginBatchEdit(); + + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); + } + final KeyboardSwitcher switcher = mKeyboardSwitcher; // The space state depends only on the last character pressed and its own previous // state. Here, we revert the space state to neutral if the key is actually modifying @@ -1328,9 +1333,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord.deactivate(); mEnteredText = null; mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); - } } // Called from PointerTracker through the KeyboardActionListener interface diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java index 11eae8813..c9f3b476a 100644 --- a/java/src/com/android/inputmethod/research/FeedbackActivity.java +++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java @@ -18,7 +18,10 @@ package com.android.inputmethod.research; import android.app.Activity; import android.os.Bundle; +import android.text.Editable; +import android.view.View; import android.widget.CheckBox; +import android.widget.EditText; import com.android.inputmethod.latin.R; @@ -28,11 +31,6 @@ public class FeedbackActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.research_feedback_activity); final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout); - final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history); - final CharSequence cs = checkbox.getText(); - final String actualString = String.format(cs.toString(), - ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE); - checkbox.setText(actualString); layout.setActivity(this); } diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java deleted file mode 100644 index 65f5f83ae..000000000 --- a/java/src/com/android/inputmethod/research/LogBuffer.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.research; - -import java.util.LinkedList; - -/** - * A buffer that holds a fixed number of LogUnits. - * - * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are - * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches - * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to - * stay under the capacity limit. - */ -public class LogBuffer { - protected final LinkedList mLogUnits; - /* package for test */ int mWordCapacity; - // The number of members of mLogUnits that are actual words. - protected int mNumActualWords; - - /** - * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and - * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. - * - * @param wordCapacity maximum number of words - */ - LogBuffer(final int wordCapacity) { - if (wordCapacity <= 0) { - throw new IllegalArgumentException("wordCapacity must be 1 or greater."); - } - mLogUnits = new LinkedList(); - mWordCapacity = wordCapacity; - mNumActualWords = 0; - } - - /** - * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's - * (oldest first) if word capacity is reached. - */ - public void shiftIn(LogUnit newLogUnit) { - if (newLogUnit.getWord() == null) { - // This LogUnit isn't a word, so it doesn't count toward the word-limit. - mLogUnits.add(newLogUnit); - return; - } - if (mNumActualWords == mWordCapacity) { - shiftOutThroughFirstWord(); - } - mLogUnits.add(newLogUnit); - mNumActualWords++; // Must be a word, or we wouldn't be here. - } - - private void shiftOutThroughFirstWord() { - while (!mLogUnits.isEmpty()) { - final LogUnit logUnit = mLogUnits.removeFirst(); - onShiftOut(logUnit); - if (logUnit.hasWord()) { - // Successfully shifted out a word-containing LogUnit and made space for the new - // LogUnit. - mNumActualWords--; - break; - } - } - } - - /** - * Removes all LogUnits from the buffer without calling onShiftOut(). - */ - public void clear() { - mLogUnits.clear(); - mNumActualWords = 0; - } - - /** - * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are - * removed in the order entered. This method is not called when shiftOut is called directly. - * - * Base class does nothing; subclasses may override. - */ - protected void onShiftOut(LogUnit logUnit) { - } - - /** - * Called to deliberately remove the oldest LogUnit. Usually called when draining the - * LogBuffer. - */ - public LogUnit shiftOut() { - if (mLogUnits.isEmpty()) { - return null; - } - final LogUnit logUnit = mLogUnits.removeFirst(); - if (logUnit.hasWord()) { - mNumActualWords--; - } - return logUnit; - } -} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java deleted file mode 100644 index 8a80664f5..000000000 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.research; - -import java.util.ArrayList; - -/** - * A group of log statements related to each other. - * - * A LogUnit is collection of LogStatements, each of which is generated by at a particular point - * in the code. (There is no LogStatement class; the data is stored across the instance variables - * here.) A single LogUnit's statements can correspond to all the calls made while in the same - * composing region, or all the calls between committing the last composing region, and the first - * character of the next composing region. - * - * Individual statements in a log may be marked as potentially private. If so, then they are only - * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit - * will not violate the user's privacy. Checks for this may include whether other LogUnits have - * been published recently, or whether the LogUnit contains numbers, etc. - */ -/* package */ class LogUnit { - private final ArrayList mKeysList = new ArrayList(); - private final ArrayList mValuesList = new ArrayList(); - private final ArrayList mIsPotentiallyPrivate = new ArrayList(); - private String mWord; - private boolean mContainsDigit; - - public void addLogStatement(final String[] keys, final Object[] values, - final Boolean isPotentiallyPrivate) { - mKeysList.add(keys); - mValuesList.add(values); - mIsPotentiallyPrivate.add(isPotentiallyPrivate); - } - - public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); - } - } - } - - public void setWord(String word) { - mWord = word; - } - - public String getWord() { - return mWord; - } - - public boolean hasWord() { - return mWord != null; - } - - public void setContainsDigit() { - mContainsDigit = true; - } - - public boolean hasDigit() { - return mContainsDigit; - } - - public boolean isEmpty() { - return mKeysList.isEmpty(); - } -} diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java deleted file mode 100644 index 745768d35..000000000 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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.research; - -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.Suggest; - -import java.util.Random; - -public class MainLogBuffer extends LogBuffer { - // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. - private static final int N_GRAM_SIZE = 2; - // The number of words between n-grams to omit from the log. - private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18; - - private final ResearchLog mResearchLog; - private Suggest mSuggest; - - // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if - // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc. - // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a - // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe - // n-gram does appear. - /* package for test */ int mMinWordPeriod; - - // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod - // after a sample is taken. - /* package for test */ int mWordsUntilSafeToSample; - - public MainLogBuffer(final ResearchLog researchLog) { - super(N_GRAM_SIZE); - mResearchLog = researchLog; - mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE; - final Random random = new Random(); - mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); - } - - public void setSuggest(Suggest suggest) { - mSuggest = suggest; - } - - @Override - public void shiftIn(final LogUnit newLogUnit) { - super.shiftIn(newLogUnit); - if (newLogUnit.hasWord()) { - if (mWordsUntilSafeToSample > 0) { - mWordsUntilSafeToSample--; - } - } - } - - public void resetWordCounter() { - mWordsUntilSafeToSample = mMinWordPeriod; - } - - /** - * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete - * form and still protect the user's privacy. - * - * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any - * non-character data that is typed between words. The decision about privacy is made based on - * the buffer's entire content. If it is decided that the privacy risks are too great to upload - * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g., - * the screen orientation and other characteristics about the device can be uploaded without - * revealing much about the user. - */ - public boolean isSafeToLog() { - // Check that we are not sampling too frequently. Having sampled recently might disclose - // too much of the user's intended meaning. - if (mWordsUntilSafeToSample > 0) { - return false; - } - if (mSuggest == null || !mSuggest.hasMainDictionary()) { - // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word - // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to - // potentially pose a privacy risk. - return false; - } - // Reload the dictionary in case it has changed (e.g., because the user has changed - // languages). - final Dictionary dictionary = mSuggest.getMainDictionary(); - if (dictionary == null) { - return false; - } - // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the - // complete buffer contents in detail. - final int length = mLogUnits.size(); - for (int i = 0; i < length; i++) { - final LogUnit logUnit = mLogUnits.get(i); - final String word = logUnit.getWord(); - if (word == null) { - // Digits outside words are a privacy threat. - if (logUnit.hasDigit()) { - return false; - } - } else { - // Words not in the dictionary are a privacy threat. - if (!(dictionary.isValidWord(word))) { - return false; - } - } - } - // All checks have passed; this buffer's content can be safely uploaded. - return true; - } - - @Override - protected void onShiftOut(LogUnit logUnit) { - if (mResearchLog != null) { - mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); - } - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 71a6d6a78..18bf3c07f 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -26,6 +26,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.ResearchLogger.LogUnit; import java.io.BufferedWriter; import java.io.File; @@ -36,7 +37,6 @@ import java.io.OutputStreamWriter; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -51,22 +51,21 @@ import java.util.concurrent.TimeUnit; */ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); - private static final boolean DEBUG = false; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; - private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; - - /* package */ final ScheduledExecutorService mExecutor; - /* package */ final File mFile; - private JsonWriter mJsonWriter = NULL_JSON_WRITER; - // true if at least one byte of data has been written out to the log file. This must be - // remembered because JsonWriter requires that calls matching calls to beginObject and - // endObject, as well as beginArray and endArray, and the file is opened lazily, only when - // it is certain that data will be written. Alternatively, the matching call exceptions - // could be caught, but this might suppress other errors. - private boolean mHasWrittenData = false; - private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( new OutputStreamWriter(new NullOutputStream())); + + final ScheduledExecutorService mExecutor; + /* package */ final File mFile; + private JsonWriter mJsonWriter = NULL_JSON_WRITER; + + private int mLoggingState; + private static final int LOGGING_STATE_UNSTARTED = 0; + private static final int LOGGING_STATE_READY = 1; // don't create file until necessary + private static final int LOGGING_STATE_RUNNING = 2; + private static final int LOGGING_STATE_STOPPING = 3; + private static final int LOGGING_STATE_STOPPED = 4; + private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static class NullOutputStream extends OutputStream { /** {@inheritDoc} */ @Override @@ -85,81 +84,128 @@ public class ResearchLog { } } - public ResearchLog(final File outputFile) { + public ResearchLog(File outputFile) { + mExecutor = Executors.newSingleThreadScheduledExecutor(); if (outputFile == null) { throw new IllegalArgumentException(); } - mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; + mLoggingState = LOGGING_STATE_UNSTARTED; } - public synchronized void close() { - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - if (mHasWrittenData) { - mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - mHasWrittenData = false; + public synchronized void start() throws IOException { + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + mLoggingState = LOGGING_STATE_READY; + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: + break; + } + } + + public synchronized void stop() { + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + mLoggingState = LOGGING_STATE_STOPPED; + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + mJsonWriter.endArray(); + mJsonWriter.flush(); + mJsonWriter.close(); + } finally { + boolean success = mFile.setWritable(false, false); + mLoggingState = LOGGING_STATE_STOPPED; + } + return null; } - } catch (Exception e) { - Log.d(TAG, "error when closing ResearchLog:"); - e.printStackTrace(); - } finally { - if (mFile.exists()) { - mFile.setWritable(false, false); - } - } - return null; - } - }); + }); + removeAnyScheduledFlush(); + mExecutor.shutdown(); + mLoggingState = LOGGING_STATE_STOPPING; + break; + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: + } + } + + public boolean isAlive() { + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + return true; + } + return false; + } + + public void waitUntilStopped(final int timeoutInMs) throws InterruptedException { removeAnyScheduledFlush(); mExecutor.shutdown(); + mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); } - private boolean mIsAbortSuccessful; - public synchronized void abort() { - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - if (mHasWrittenData) { - mJsonWriter.endArray(); - mJsonWriter.close(); - mHasWrittenData = false; + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + mLoggingState = LOGGING_STATE_STOPPED; + isAbortSuccessful = true; + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + mJsonWriter.endArray(); + mJsonWriter.close(); + } finally { + isAbortSuccessful = mFile.delete(); + } + return null; } - } finally { - mIsAbortSuccessful = mFile.delete(); - } - return null; - } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); + }); + removeAnyScheduledFlush(); + mExecutor.shutdown(); + mLoggingState = LOGGING_STATE_STOPPING; + break; + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: + } } - public boolean blockingAbort() throws InterruptedException { - abort(); - mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - return mIsAbortSuccessful; - } - - public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException { - mExecutor.awaitTermination(delay, timeUnit); + private boolean isAbortSuccessful; + public boolean isAbortSuccessful() { + return isAbortSuccessful; } /* package */ synchronized void flush() { - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + removeAnyScheduledFlush(); + mExecutor.submit(mFlushCallable); + break; + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: + } } - private final Callable mFlushCallable = new Callable() { + private Callable mFlushCallable = new Callable() { @Override public Object call() throws Exception { - mJsonWriter.flush(); + if (mLoggingState == LOGGING_STATE_RUNNING) { + mJsonWriter.flush(); + } return null; } }; @@ -178,40 +224,56 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } - public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) { - try { - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - logUnit.publishTo(ResearchLog.this, isIncludingPrivateData); - scheduleFlush(); - return null; - } - }); - } catch (RejectedExecutionException e) { - // TODO: Add code to record loss of data, and report. + public synchronized void publishPublicEvents(final LogUnit logUnit) { + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + logUnit.publishPublicEventsTo(ResearchLog.this); + scheduleFlush(); + return null; + } + }); + break; + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: + } + } + + public synchronized void publishAllEvents(final LogUnit logUnit) { + switch (mLoggingState) { + case LOGGING_STATE_UNSTARTED: + break; + case LOGGING_STATE_READY: + case LOGGING_STATE_RUNNING: + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + logUnit.publishAllEventsTo(ResearchLog.this); + scheduleFlush(); + return null; + } + }); + break; + case LOGGING_STATE_STOPPING: + case LOGGING_STATE_STOPPED: } } private static final String CURRENT_TIME_KEY = "_ct"; private static final String UPTIME_KEY = "_ut"; private static final String EVENT_TYPE_KEY = "_ty"; - void outputEvent(final String[] keys, final Object[] values) { - // Not thread safe. - if (keys.length == 0) { - return; - } - if (DEBUG) { - if (keys.length != values.length + 1) { - Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); - } - } + // not thread safe. try { if (mJsonWriter == NULL_JSON_WRITER) { mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); + mJsonWriter.setLenient(true); mJsonWriter.beginArray(); - mHasWrittenData = true; } mJsonWriter.beginObject(); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); @@ -221,8 +283,8 @@ public class ResearchLog { for (int i = 0; i < length; i++) { mJsonWriter.name(keys[i + 1]); Object value = values[i]; - if (value instanceof CharSequence) { - mJsonWriter.value(value.toString()); + if (value instanceof String) { + mJsonWriter.value((String) value); } else if (value instanceof Number) { mJsonWriter.value((Number) value); } else if (value instanceof Boolean) { @@ -269,11 +331,14 @@ public class ResearchLog { SuggestedWords words = (SuggestedWords) value; mJsonWriter.beginObject(); mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); + mJsonWriter.name("willAutoCorrect") + .value(words.mWillAutoCorrect); mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction").value(words.mIsPrediction); + .value(words.mIsPunctuationSuggestions); + mJsonWriter.name("isObsoleteSuggestions") + .value(words.mIsObsoleteSuggestions); + mJsonWriter.name("isPrediction") + .value(words.mIsPrediction); mJsonWriter.name("words"); mJsonWriter.beginArray(); final int size = words.size(); @@ -298,8 +363,8 @@ public class ResearchLog { try { mJsonWriter.close(); } catch (IllegalStateException e1) { - // Assume that this is just the json not being terminated properly. - // Ignore + // assume that this is just the json not being terminated properly. + // ignore } catch (IOException e1) { e1.printStackTrace(); } finally { diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java index 9904a1de2..3b1213009 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -27,6 +27,7 @@ import android.os.BatteryManager; import android.util.Log; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.R.string; import java.io.BufferedReader; import java.io.File; @@ -47,7 +48,6 @@ public final class ResearchLogUploader { private static final String TAG = ResearchLogUploader.class.getSimpleName(); private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min private static final int BUF_SIZE = 1024 * 8; - protected static final int TIMEOUT_IN_MS = 1000 * 4; private final boolean mCanUpload; private final Context mContext; @@ -55,6 +55,8 @@ public final class ResearchLogUploader { private final URL mUrl; private final ScheduledExecutorService mExecutor; + private Runnable doUploadRunnable = new UploadRunnable(null, false); + public ResearchLogUploader(final Context context, final File filesDir) { mContext = context; mFilesDir = filesDir; @@ -91,15 +93,11 @@ public final class ResearchLogUploader { public void start() { if (mCanUpload) { - mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, - null /* callback */, false /* forceUpload */), - UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } - } - - public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { - if (mCanUpload) { - mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); + Log.d(TAG, "scheduling regular uploading"); + mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS, + UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } else { + Log.d(TAG, "no permission to upload"); } } @@ -108,8 +106,7 @@ public final class ResearchLogUploader { // another upload happening right now, as it may have missed the latest changes. // TODO: Reschedule regular upload tests starting from now. if (mCanUpload) { - mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, - true /* forceUpload */)); + mExecutor.submit(new UploadRunnable(callback, true)); } } @@ -133,33 +130,19 @@ public final class ResearchLogUploader { } class UploadRunnable implements Runnable { - private final ResearchLog mLogToWaitFor; private final Callback mCallback; private final boolean mForceUpload; - public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, - final boolean forceUpload) { - mLogToWaitFor = logToWaitFor; + public UploadRunnable(final Callback callback, final boolean forceUpload) { mCallback = callback; mForceUpload = forceUpload; } @Override public void run() { - if (mLogToWaitFor != null) { - waitFor(mLogToWaitFor); - } doUpload(); } - private void waitFor(final ResearchLog researchLog) { - try { - researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - private void doUpload() { if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { return; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 3cad2d099..b5bdc1958 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -67,8 +67,11 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; +import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.UUID; @@ -94,21 +97,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); private static final boolean IS_SHOWING_INDICATOR = true; private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false; - public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; + private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user private static final ResearchLogger sInstance = new ResearchLogger(); // to write to a different filename, e.g., for testing, set mFile before calling start() /* package */ File mFilesDir; /* package */ String mUUIDString; /* package */ ResearchLog mMainResearchLog; - /* package */ ResearchLog mFeedbackLog; - /* package */ MainLogBuffer mMainLogBuffer; - /* package */ LogBuffer mFeedbackLogBuffer; + // The mIntentionalResearchLog records all events for the session, private or not (excepting + // passwords). It is written to permanent storage only if the user explicitly commands + // the system to do so. + /* package */ ResearchLog mIntentionalResearchLog; + // LogUnits are queued here and released only when the user requests the intentional log. + private List mIntentionalResearchLogQueue = new ArrayList(); private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -133,9 +139,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; private final Statistics mStatistics; - private ResearchLogUploader mResearchLogUploader; - private LogUnit mCurrentLogUnit = new LogUnit(); + private ResearchLogUploader mResearchLogUploader; private ResearchLogger() { mStatistics = Statistics.getInstance(); @@ -259,16 +264,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; - } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); - sIsLogging = enableLogging; - } - private File createLogFile(File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(FILENAME_PREFIX).append('-'); @@ -315,58 +310,97 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); return; } - if (mMainLogBuffer == null) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - mMainLogBuffer = new MainLogBuffer(mMainResearchLog); - mMainLogBuffer.setSuggest(mSuggest); - } - if (mFeedbackLogBuffer == null) { - mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); - // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold - // the feedback LogUnit itself. - mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); + try { + if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { + mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); + } + mMainResearchLog.start(); + if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { + mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); + } + mIntentionalResearchLog.start(); + } catch (IOException e) { + Log.w(TAG, "Could not start ResearchLogger."); } } /* package */ void stop() { logStatistics(); - commitCurrentLogUnit(); + publishLogUnit(mCurrentLogUnit, true); + mCurrentLogUnit = new LogUnit(); - if (mMainLogBuffer != null) { - publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */); - mMainResearchLog.close(); - mMainLogBuffer = null; + if (mMainResearchLog != null) { + mMainResearchLog.stop(); } - if (mFeedbackLogBuffer != null) { - mFeedbackLog.close(); - mFeedbackLogBuffer = null; + if (mIntentionalResearchLog != null) { + mIntentionalResearchLog.stop(); } } + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } + + private void setLoggingAllowed(boolean enableLogging) { + if (mPrefs == null) { + return; + } + Editor e = mPrefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); + e.apply(); + sIsLogging = enableLogging; + } + public boolean abort() { boolean didAbortMainLog = false; - if (mMainLogBuffer != null) { - mMainLogBuffer.clear(); + if (mMainResearchLog != null) { + mMainResearchLog.abort(); try { - didAbortMainLog = mMainResearchLog.blockingAbort(); + mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); } catch (InterruptedException e) { - // Don't know whether this succeeded or not. We assume not; this is reported - // to the caller. + // interrupted early. carry on. } - mMainLogBuffer = null; + if (mMainResearchLog.isAbortSuccessful()) { + didAbortMainLog = true; + } + mMainResearchLog = null; } - boolean didAbortFeedbackLog = false; - if (mFeedbackLogBuffer != null) { - mFeedbackLogBuffer.clear(); + boolean didAbortIntentionalLog = false; + if (mIntentionalResearchLog != null) { + mIntentionalResearchLog.abort(); try { - didAbortFeedbackLog = mFeedbackLog.blockingAbort(); + mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); } catch (InterruptedException e) { - // Don't know whether this succeeded or not. We assume not; this is reported - // to the caller. + // interrupted early. carry on. } - mFeedbackLogBuffer = null; + if (mIntentionalResearchLog.isAbortSuccessful()) { + didAbortIntentionalLog = true; + } + mIntentionalResearchLog = null; + } + return didAbortMainLog && didAbortIntentionalLog; + } + + /* package */ void flush() { + if (mMainResearchLog != null) { + mMainResearchLog.flush(); } - return didAbortMainLog && didAbortFeedbackLog; } private void restart() { @@ -470,39 +504,79 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } - private static final String[] EVENTKEYS_FEEDBACK = { - "UserTimestamp", "contents" - }; + private ResearchLog mFeedbackLog; + private List mFeedbackQueue; + private ResearchLog mSavedMainResearchLog; + private ResearchLog mSavedIntentionalResearchLog; + private List mSavedIntentionalResearchLogQueue; + + private void saveLogsForFeedback() { + mFeedbackLog = mIntentionalResearchLog; + if (mIntentionalResearchLogQueue != null) { + mFeedbackQueue = new ArrayList(mIntentionalResearchLogQueue); + } else { + mFeedbackQueue = null; + } + mSavedMainResearchLog = mMainResearchLog; + mSavedIntentionalResearchLog = mIntentionalResearchLog; + mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue; + + mMainResearchLog = null; + mIntentionalResearchLog = null; + mIntentionalResearchLogQueue = new ArrayList(); + } + + private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (mFeedbackLogBuffer == null) { - return; + if (includeHistory && mFeedbackLog != null) { + try { + LogUnit headerLogUnit = new LogUnit(); + headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false); + mFeedbackLog.publishAllEvents(headerLogUnit); + for (LogUnit logUnit : mFeedbackQueue) { + mFeedbackLog.publishAllEvents(logUnit); + } + userFeedback(mFeedbackLog, feedbackContents); + mFeedbackLog.stop(); + try { + mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); + mIntentionalResearchLog.start(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + mIntentionalResearchLogQueue.clear(); + } + mResearchLogUploader.uploadNow(null); + } else { + // create a separate ResearchLog just for feedback + final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir)); + try { + feedbackLog.start(); + userFeedback(feedbackLog, feedbackContents); + feedbackLog.stop(); + feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); + mResearchLogUploader.uploadNow(null); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - if (!includeHistory) { - mFeedbackLogBuffer.clear(); - } - commitCurrentLogUnit(); - final LogUnit feedbackLogUnit = new LogUnit(); - final Object[] values = { - feedbackContents - }; - feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values, - false /* isPotentiallyPrivate */); - mFeedbackLogBuffer.shiftIn(feedbackLogUnit); - publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); - mFeedbackLog.close(); - mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); - mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; + mMainResearchLog = mSavedMainResearchLog; + mIntentionalResearchLog = mSavedIntentionalResearchLog; + mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue; } public void initSuggest(Suggest suggest) { mSuggest = suggest; - if (mMainLogBuffer != null) { - mMainLogBuffer.setSuggest(mSuggest); - } } private void setIsPasswordView(boolean isPasswordView) { @@ -510,7 +584,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; + return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging; } public void requestIndicatorRedraw() { @@ -557,8 +631,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private static final String CURRENT_TIME_KEY = "_ct"; + private static final String UPTIME_KEY = "_ut"; + private static final String EVENT_TYPE_KEY = "_ty"; private static final Object[] EVENTKEYS_NULLVALUES = {}; + private LogUnit mCurrentLogUnit = new LogUnit(); + /** * Buffer a research log event, flagging it as privacy-sensitive. * @@ -574,14 +653,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */); + mCurrentLogUnit.addLogAtom(keys, values, true); } } - private void setCurrentLogUnitContainsDigitFlag() { - mCurrentLogUnit.setContainsDigit(); - } - /** * Buffer a research log event, flaggint it as not privacy-sensitive. * @@ -597,54 +672,140 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void enqueueEvent(final String[] keys, final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */); + mCurrentLogUnit.addLogAtom(keys, values, false); } } - /* package for test */ void commitCurrentLogUnit() { - if (!mCurrentLogUnit.isEmpty()) { - if (mMainLogBuffer != null) { - mMainLogBuffer.shiftIn(mCurrentLogUnit); - if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) { - publishLogBuffer(mMainLogBuffer, mMainResearchLog, - true /* isIncludingPrivateData */); - mMainLogBuffer.resetWordCounter(); + // Used to track how often words are logged. Too-frequent logging can leak + // semantics, disclosing private data. + /* package for test */ static class LoggingFrequencyState { + private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; + private int mWordsRemainingToSkip; + private final int mFrequency; + + /** + * Tracks how often words may be uploaded. + * + * @param frequency 1=Every word, 2=Every other word, etc. + */ + public LoggingFrequencyState(int frequency) { + mFrequency = frequency; + mWordsRemainingToSkip = mFrequency; + } + + public void onWordLogged() { + mWordsRemainingToSkip = mFrequency; + } + + public void onWordNotLogged() { + if (mWordsRemainingToSkip > 1) { + mWordsRemainingToSkip--; + } + } + + public boolean isSafeToLog() { + return mWordsRemainingToSkip <= 1; + } + } + + /* package for test */ LoggingFrequencyState mLoggingFrequencyState = + new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); + + /* package for test */ boolean isPrivacyThreat(String word) { + // Current checks: + // - Word not in dictionary + // - Word contains numbers + // - Privacy-safe word not logged recently + if (TextUtils.isEmpty(word)) { + return false; + } + if (!mLoggingFrequencyState.isSafeToLog()) { + return true; + } + final int length = word.length(); + boolean hasLetter = false; + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = Character.codePointAt(word, i); + if (Character.isDigit(codePoint)) { + return true; + } + if (Character.isLetter(codePoint)) { + hasLetter = true; + break; // Word may contain digits, but will only be allowed if in the dictionary. + } + } + if (hasLetter) { + if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { + mDictionary = mSuggest.getMainDictionary(); + } + if (mDictionary == null) { + // Can't access dictionary. Assume privacy threat. + return true; + } + return !(mDictionary.isValidWord(word)); + } + // No letters, no numbers. Punctuation, space, or something else. + return false; + } + + private void onWordComplete(String word) { + if (isPrivacyThreat(word)) { + publishLogUnit(mCurrentLogUnit, true); + mLoggingFrequencyState.onWordNotLogged(); + } else { + publishLogUnit(mCurrentLogUnit, false); + mLoggingFrequencyState.onWordLogged(); + } + mCurrentLogUnit = new LogUnit(); + mStatistics.recordWordEntered(); + } + + private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { + if (!isAllowedToLog()) { + return; + } + if (mMainResearchLog == null) { + return; + } + if (isPrivacySensitive) { + mMainResearchLog.publishPublicEvents(logUnit); + } else { + mMainResearchLog.publishAllEvents(logUnit); + } + mIntentionalResearchLogQueue.add(logUnit); + } + + /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) { + publishLogUnit(mCurrentLogUnit, isPrivacySensitive); + } + + static class LogUnit { + private final List mKeysList = new ArrayList(); + private final List mValuesList = new ArrayList(); + private final List mIsPotentiallyPrivate = new ArrayList(); + + private void addLogAtom(final String[] keys, final Object[] values, + final Boolean isPotentiallyPrivate) { + mKeysList.add(keys); + mValuesList.add(values); + mIsPotentiallyPrivate.add(isPotentiallyPrivate); + } + + public void publishPublicEventsTo(ResearchLog researchLog) { + final int size = mKeysList.size(); + for (int i = 0; i < size; i++) { + if (!mIsPotentiallyPrivate.get(i)) { + researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); } } - if (mFeedbackLogBuffer != null) { - mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); - } - mCurrentLogUnit = new LogUnit(); - Log.d(TAG, "commitCurrentLogUnit"); } - } - /* package for test */ void publishLogBuffer(final LogBuffer logBuffer, - final ResearchLog researchLog, final boolean isIncludingPrivateData) { - LogUnit logUnit; - while ((logUnit = logBuffer.shiftOut()) != null) { - researchLog.publish(logUnit, isIncludingPrivateData); - } - } - - private boolean hasOnlyLetters(final String word) { - final int length = word.length(); - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = word.codePointAt(i); - if (!Character.isLetter(codePoint)) { - return false; + public void publishAllEventsTo(ResearchLog researchLog) { + final int size = mKeysList.size(); + for (int i = 0; i < size; i++) { + researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); } } - return true; - } - - private void onWordComplete(final String word) { - Log.d(TAG, "onWordComplete: " + word); - if (word != null && word.length() > 0 && hasOnlyLetters(word)) { - mCurrentLogUnit.setWord(word); - mStatistics.recordWordEntered(); - } - commitCurrentLogUnit(); } private static int scrubDigitFromCodePoint(int codePoint) { @@ -697,6 +858,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return WORD_REPLACEMENT_STRING; } + // Special methods related to startup, shutdown, logging itself + + private static final String[] EVENTKEYS_INTENTIONAL_LOG = { + "IntentionalLog" + }; + private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" @@ -704,6 +871,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs) { final ResearchLogger researchLogger = getInstance(); + if (researchLogger.mInFeedbackDialog) { + researchLogger.saveLogsForFeedback(); + } researchLogger.start(); if (editorInfo != null) { final Context context = researchLogger.mInputMethodService; @@ -735,6 +905,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; + private void userFeedback(ResearchLog researchLog, String feedbackContents) { + // this method is special; it directs the feedbackContents to a particular researchLog + final LogUnit logUnit = new LogUnit(); + final Object[] values = { + feedbackContents + }; + logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false); + researchLog.publishAllEvents(logUnit); + } + // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { @@ -769,16 +949,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEOnCodeInput", "code", "x", "y" }; public static void latinIME_onCodeInput(final int code, final int x, final int y) { - final long time = SystemClock.uptimeMillis(); - final ResearchLogger researchLogger = getInstance(); final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; + final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - if (Character.isDigit(code)) { - researchLogger.setCurrentLogUnitContainsDigitFlag(); - } - researchLogger.mStatistics.recordChar(code, time); + researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { @@ -843,7 +1019,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); - researchLogger.commitCurrentLogUnit(); + // Play it safe. Remove privacy-sensitive events. + researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); + researchLogger.mCurrentLogUnit = new LogUnit(); getInstance().stop(); } } @@ -908,11 +1086,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)) }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); - if (Character.isDigit(code)) { - researchLogger.setCurrentLogUnitContainsDigitFlag(); - } + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { @@ -1050,21 +1224,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); } - // Disabled for privacy-protection reasons. Because this event comes after - // richInputConnection_commitText, which is the event used to separate LogUnits, the - // data in this event can be associated with the next LogUnit, revealing information - // about the current word even if it was supposed to be suppressed. The occurrance of - // autocorrection can be determined by examining the difference between the text strings in - // the last call to richInputConnection_setComposingText before - // richInputConnection_commitText, so it's not a data loss. - // TODO: Figure out how to log this event without loss of privacy. - /* private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { - "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection" + "RichInputConnectionCommitCorrection", "CorrectionInfo" }; - */ public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { - /* final String typedWord = correctionInfo.getOldText().toString(); final String autoCorrection = correctionInfo.getNewText().toString(); final Object[] values = { @@ -1073,7 +1236,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); - */ } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { @@ -1099,8 +1261,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { beforeLength, afterLength }; - getInstance().enqueuePotentiallyPrivateEvent( - EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { @@ -1130,8 +1291,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang keyEvent.getAction(), keyEvent.getKeyCode() }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, - values); + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { @@ -1139,14 +1299,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; public static void richInputConnection_setComposingText(final CharSequence text, final int newCursorPosition) { - if (text == null) { - throw new RuntimeException("setComposingText is null"); - } final Object[] values = { text, newCursorPosition }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, - values); + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { @@ -1156,8 +1312,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { from, to }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, - values); + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); } private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { @@ -1192,24 +1347,4 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void userTimestamp() { getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); } - - private static final String[] EVENTKEYS_STATISTICS = { - "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", - "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", - "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" - }; - private static void logStatistics() { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - final Object[] values = { - statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, - statistics.mSpaceCount, statistics.mDeleteKeyCount, - statistics.mWordCount, statistics.mIsEmptyUponStarting, - statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), - statistics.mBeforeDeleteKeyCounter.getAverageTime(), - statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), - statistics.mAfterDeleteKeyCounter.getAverageTime() - }; - researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); - } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index eab465aa2..4a2cd079c 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -66,8 +66,8 @@ public class Statistics { // To account for the interruptions when the user's attention is directed elsewhere, times // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds + public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds // The last time that a tap was performed private long mLastTapTime; From 77257a736ea920e7e618cb0d1b850b544283d439 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 20 Aug 2012 10:17:43 -0700 Subject: [PATCH 086/133] Revert "ResearchLogger to track simple statistics" This reverts commit 7cec911fde16775c28db3dbb16b064fc7ce69b55 Change-Id: I3eb055e03a9cf463356ebaefc7827ab65a963072 --- .../inputmethod/research/ResearchLogger.java | 57 +------ .../inputmethod/research/Statistics.java | 146 ------------------ 2 files changed, 1 insertion(+), 202 deletions(-) delete mode 100644 java/src/com/android/inputmethod/research/Statistics.java diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b5bdc1958..98ed53656 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -34,7 +34,6 @@ import android.graphics.Paint.Style; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.IBinder; -import android.os.SystemClock; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; @@ -138,12 +137,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private Dictionary mDictionary; private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; - private final Statistics mStatistics; private ResearchLogUploader mResearchLogUploader; private ResearchLogger() { - mStatistics = Statistics.getInstance(); } public static ResearchLogger getInstance() { @@ -273,35 +270,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } - private void checkForEmptyEditor() { - if (mInputMethodService == null) { - return; - } - final InputConnection ic = mInputMethodService.getCurrentInputConnection(); - if (ic == null) { - return; - } - final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); - if (!TextUtils.isEmpty(textBefore)) { - mStatistics.setIsEmptyUponStarting(false); - return; - } - final CharSequence textAfter = ic.getTextAfterCursor(1, 0); - if (!TextUtils.isEmpty(textAfter)) { - mStatistics.setIsEmptyUponStarting(false); - return; - } - if (textBefore != null && textAfter != null) { - mStatistics.setIsEmptyUponStarting(true); - } - } - private void start() { maybeShowSplashScreen(); updateSuspendedState(); requestIndicatorRedraw(); - mStatistics.reset(); - checkForEmptyEditor(); if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; @@ -325,10 +297,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /* package */ void stop() { - logStatistics(); - publishLogUnit(mCurrentLogUnit, true); - mCurrentLogUnit = new LogUnit(); - if (mMainResearchLog != null) { mMainResearchLog.stop(); } @@ -337,26 +305,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private static final String[] EVENTKEYS_STATISTICS = { - "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", - "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", - "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" - }; - private static void logStatistics() { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - final Object[] values = { - statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, - statistics.mSpaceCount, statistics.mDeleteKeyCount, - statistics.mWordCount, statistics.mIsEmptyUponStarting, - statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), - statistics.mBeforeDeleteKeyCounter.getAverageTime(), - statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), - statistics.mAfterDeleteKeyCounter.getAverageTime() - }; - researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); - } - private void setLoggingAllowed(boolean enableLogging) { if (mPrefs == null) { return; @@ -757,7 +705,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mLoggingFrequencyState.onWordLogged(); } mCurrentLogUnit = new LogUnit(); - mStatistics.recordWordEntered(); } private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { @@ -952,9 +899,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java deleted file mode 100644 index 4a2cd079c..000000000 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.research; - -import com.android.inputmethod.keyboard.Keyboard; - -public class Statistics { - // Number of characters entered during a typing session - int mCharCount; - // Number of letter characters entered during a typing session - int mLetterCount; - // Number of number characters entered - int mNumberCount; - // Number of space characters entered - int mSpaceCount; - // Number of delete operations entered (taps on the backspace key) - int mDeleteKeyCount; - // Number of words entered during a session. - int mWordCount; - // Whether the text field was empty upon editing - boolean mIsEmptyUponStarting; - boolean mIsEmptinessStateKnown; - - // Timers to count average time to enter a key, first press a delete key, - // between delete keys, and then to return typing after a delete key. - final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); - final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); - final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); - final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); - - static class AverageTimeCounter { - int mCount; - int mTotalTime; - - public void reset() { - mCount = 0; - mTotalTime = 0; - } - - public void add(long deltaTime) { - mCount++; - mTotalTime += deltaTime; - } - - public int getAverageTime() { - if (mCount == 0) { - return 0; - } - return mTotalTime / mCount; - } - } - - // To account for the interruptions when the user's attention is directed elsewhere, times - // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds - - // The last time that a tap was performed - private long mLastTapTime; - // The type of the last keypress (delete key or not) - boolean mIsLastKeyDeleteKey; - - private static final Statistics sInstance = new Statistics(); - - public static Statistics getInstance() { - return sInstance; - } - - private Statistics() { - reset(); - } - - public void reset() { - mCharCount = 0; - mLetterCount = 0; - mNumberCount = 0; - mSpaceCount = 0; - mDeleteKeyCount = 0; - mWordCount = 0; - mIsEmptyUponStarting = true; - mIsEmptinessStateKnown = false; - mKeyCounter.reset(); - mBeforeDeleteKeyCounter.reset(); - mDuringRepeatedDeleteKeysCounter.reset(); - mAfterDeleteKeyCounter.reset(); - - mLastTapTime = 0; - mIsLastKeyDeleteKey = false; - } - - public void recordChar(int codePoint, long time) { - final long delta = time - mLastTapTime; - if (codePoint == Keyboard.CODE_DELETE) { - mDeleteKeyCount++; - if (delta < MIN_DELETION_INTERMISSION) { - if (mIsLastKeyDeleteKey) { - mDuringRepeatedDeleteKeysCounter.add(delta); - } else { - mBeforeDeleteKeyCounter.add(delta); - } - } - mIsLastKeyDeleteKey = true; - } else { - mCharCount++; - if (Character.isDigit(codePoint)) { - mNumberCount++; - } - if (Character.isLetter(codePoint)) { - mLetterCount++; - } - if (Character.isSpaceChar(codePoint)) { - mSpaceCount++; - } - if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { - mAfterDeleteKeyCounter.add(delta); - } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { - mKeyCounter.add(delta); - } - mIsLastKeyDeleteKey = false; - } - mLastTapTime = time; - } - - public void recordWordEntered() { - mWordCount++; - } - - public void setIsEmptyUponStarting(final boolean isEmpty) { - mIsEmptyUponStarting = isEmpty; - mIsEmptinessStateKnown = true; - } -} From 35c11842a78e272ac4f61bfc58b96826c833f104 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 20 Aug 2012 10:17:45 -0700 Subject: [PATCH 087/133] Revert "ResearchLogger log RichInputConnection" This reverts commit 1964ffb3f43f0ada429d13b41bd22b90a97fa9fc Change-Id: Ie43e7ac828faa91e1824a255bb3ecf6c8222d4e8 --- .../android/inputmethod/latin/LatinIME.java | 76 +++++-- .../latin/RichInputConnection.java | 86 +++---- .../inputmethod/research/ResearchLogger.java | 211 ++++++++---------- 3 files changed, 184 insertions(+), 189 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a4c82c9f7..c20f3a3a9 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -907,13 +907,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(null); - } return; } @@ -935,9 +935,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1058,6 +1055,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(typedWord); + } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1099,9 +1099,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpace(); + ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); } mKeyboardSwitcher.updateShiftState(); } @@ -1118,6 +1121,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_doubleSpaceAutoPeriod(); + } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1181,6 +1187,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_performEditorAction(actionId); + } } private void handleLanguageSwitchKey() { @@ -1217,9 +1226,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } return; } @@ -1236,6 +1242,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } } // Implementation of {@link KeyboardActionListener}. @@ -1346,6 +1355,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(text); + } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1444,6 +1456,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(length); + } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1463,6 +1478,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1491,6 +1509,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); + } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1509,8 +1530,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1835,6 +1862,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, + autoCorrection.toString()); + } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1860,13 +1891,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); + } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); - } return; } @@ -1893,6 +1924,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, + completionInfo.getText(), x, y); + } return; } @@ -1901,12 +1936,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - mExpectingUpdateSelection = true; - commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, - LastComposedWord.NOT_A_SEPARATOR); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); } + mExpectingUpdateSelection = true; + commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, + LastComposedWord.NOT_A_SEPARATOR); mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1941,6 +1976,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(chosenWord); + } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2008,6 +2046,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(length); + } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2035,6 +2076,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(deleteLength); + } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 41e59e92d..8b4c17322 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,9 +55,7 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) { - mIC.beginBatchEdit(); - } + if (null != mIC) mIC.beginBatchEdit(); } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -68,9 +66,7 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) { - mIC.endBatchEdit(); - } + if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); } private void checkBatchEdit() { @@ -83,22 +79,12 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) { - mIC.finishComposingText(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_finishComposingText(); - } - } + if (null != mIC) mIC.finishComposingText(); } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) { - mIC.commitText(text, i); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitText(text, i); - } - } + if (null != mIC) mIC.commitText(text, i); } public int getCursorCapsMode(final int inputType) { @@ -121,72 +107,37 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) { - mIC.deleteSurroundingText(i, j); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_deleteSurroundingText(i, j); - } - } + if (null != mIC) mIC.deleteSurroundingText(i, j); } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) { - mIC.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_performEditorAction(actionId); - } - } + if (null != mIC) mIC.performEditorAction(actionId); } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) { - mIC.sendKeyEvent(keyEvent); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); - } - } + if (null != mIC) mIC.sendKeyEvent(keyEvent); } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) { - mIC.setComposingText(text, i); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_setComposingText(text, i); - } - } + if (null != mIC) mIC.setComposingText(text, i); } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) { - mIC.setSelection(from, to); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_setSelection(from, to); - } - } + if (null != mIC) mIC.setSelection(from, to); } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) { - mIC.commitCorrection(correctionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitCorrection(correctionInfo); - } - } + if (null != mIC) mIC.commitCorrection(correctionInfo); } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) { - mIC.commitCompletion(completionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitCompletion(completionInfo); - } - } + if (null != mIC) mIC.commitCompletion(completionInfo); } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -364,6 +315,9 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } @@ -428,7 +382,13 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } commitText(" ", 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); + } return true; } @@ -449,7 +409,13 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertSwapPunctuation(); + } return true; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 98ed53656..cf6f31a0a 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,14 +37,12 @@ import android.os.IBinder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -848,6 +846,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } + private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { + "LatinIMECommitText", "typedWord" + }; + + public static void latinIME_commitText(final CharSequence typedWord) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); + researchLogger.onWordComplete(scrubbedWord); + } + private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -902,6 +914,47 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } + private static final String[] EVENTKEYS_CORRECTION = { + "LogCorrection", "subgroup", "before", "after", "position" + }; + public static void logCorrection(final String subgroup, final String before, final String after, + final int position) { + final Object[] values = { + subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { + "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" + }; + public static void latinIME_commitCurrentAutoCorrection(final String typedWord, + final String autoCorrection) { + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { + "LatinIMEDeleteSurroundingText", "length" + }; + public static void latinIME_deleteSurroundingText(final int length) { + final Object[] values = { + length + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { + "LatinIMEDoubleSpaceAutoPeriod" + }; + public static void latinIME_doubleSpaceAutoPeriod() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); + } + private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" }; @@ -926,10 +979,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { - // Capture the TextView contents. This will trigger onUpdateSelection(), so we - // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, - // it can tell that it was generated by the logging code, and not by the user, and - // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -999,6 +1048,29 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } + private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { + "LatinIMEPerformEditorAction", "imeActionNext" + }; + public static void latinIME_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { + "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" + }; + public static void latinIME_pickApplicationSpecifiedCompletion(final int index, + final CharSequence cs, int x, int y) { + final Object[] values = { + index, cs, x, y + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); + } + private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1024,6 +1096,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } + private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { + "LatinIMERevertDoubleSpaceWhileInBatchEdit" + }; + public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { + "LatinIMERevertSwapPunctuation" + }; + public static void latinIME_revertSwapPunctuation() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); + } + private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1034,11 +1121,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { - "LatinIMESwapSwapperAndSpace" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { + "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" }; - public static void latinIME_swapSwapperAndSpace() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1157,109 +1245,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { - "RichInputConnectionCommitCompletion", "completionInfo" - }; - public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { - final Object[] values = { - completionInfo - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { - "RichInputConnectionCommitCorrection", "CorrectionInfo" - }; - public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { - final String typedWord = correctionInfo.getOldText().toString(); - final String autoCorrection = correctionInfo.getNewText().toString(); - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { - "RichInputConnectionCommitText", "typedWord", "newCursorPosition" - }; - public static void richInputConnection_commitText(final CharSequence typedWord, - final int newCursorPosition) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord, newCursorPosition - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, - values); - researchLogger.onWordComplete(scrubbedWord); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { - "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" - }; - public static void richInputConnection_deleteSurroundingText(final int beforeLength, - final int afterLength) { - final Object[] values = { - beforeLength, afterLength - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { - "RichInputConnectionFinishComposingText" - }; - public static void richInputConnection_finishComposingText() { - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { - "RichInputConnectionPerformEditorAction", "imeActionNext" - }; - public static void richInputConnection_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { - "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" - }; - public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { - final Object[] values = { - keyEvent.getEventTime(), - keyEvent.getAction(), - keyEvent.getKeyCode() - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { - "RichInputConnectionSetComposingText", "text", "newCursorPosition" - }; - public static void richInputConnection_setComposingText(final CharSequence text, - final int newCursorPosition) { - final Object[] values = { - text, newCursorPosition - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { - "RichInputConnectionSetSelection", "from", "to" - }; - public static void richInputConnection_setSelection(final int from, final int to) { - final Object[] values = { - from, to - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); - } - private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; From 8d0cf41f49b1962ecc255de13fe35d7f0a8c2f3d Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 21 Aug 2012 10:42:49 +0900 Subject: [PATCH 088/133] Fix NPE Bug: 7017430 Change-Id: Ie46f02a934763c3b6d65d0d62a8239d80eaefabd --- .../inputmethod/keyboard/KeyboardView.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index ccbb081b4..fe14c3ffc 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -30,6 +30,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Message; import android.util.AttributeSet; +import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.LayoutInflater; @@ -78,6 +79,8 @@ import java.util.HashSet; * @attr ref R.styleable#KeyboardView_shadowRadius */ public class KeyboardView extends View implements PointerTracker.DrawingProxy { + private static final String TAG = KeyboardView.class.getSimpleName(); + // Miscellaneous constants private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; @@ -932,9 +935,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final int[] viewOrigin = new int[2]; getLocationInWindow(viewOrigin); mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]); - final ViewGroup windowContentView = - (ViewGroup)getRootView().findViewById(android.R.id.content); - windowContentView.addView(mPreviewPlacerView); + final View rootView = getRootView(); + if (rootView == null) { + Log.w(TAG, "Cannot find root view"); + return; + } + final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content); + // Note: It'd be very weird if we get null by android.R.id.content. + if (windowContentView == null) { + Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView"); + } else { + windowContentView.addView(mPreviewPlacerView); + } } public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) { From 6785b9072762e15bb49657ce7b7d228dab76e44a Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 21 Aug 2012 17:13:45 +0900 Subject: [PATCH 089/133] Remove touch coordinate from pickSuggestionManually Change-Id: I33b1b4bc6fdfd263c4b2bb7125c07b4bddcf3c9f --- .../android/inputmethod/latin/LatinIME.java | 11 +++++----- .../suggestions/SuggestionStripView.java | 8 +++---- .../inputmethod/research/ResearchLogger.java | 21 ++++++++++++------- .../inputmethod/latin/InputTestsBase.java | 4 +--- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a7896ad52..58df15929 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1883,8 +1883,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} // interface @Override - public void pickSuggestionManually(final int index, final CharSequence suggestion, - final int x, final int y) { + public void pickSuggestionManually(final int index, final CharSequence suggestion) { final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && isShowingPunctuationList()) { @@ -1893,7 +1892,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y); + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, @@ -1926,8 +1925,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, - completionInfo.getText(), x, y); + ResearchLogger.latinIME_pickApplicationSpecifiedCompletion( + index, completionInfo.getText()); } return; } @@ -1938,7 +1937,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y); + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index b57ffd2de..bd9a0da13 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -72,7 +72,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen OnLongClickListener { public interface Listener { public boolean addWordToUserDictionary(String word); - public void pickSuggestionManually(int index, CharSequence word, int x, int y); + public void pickSuggestionManually(int index, CharSequence word); } // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. @@ -726,9 +726,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen public boolean onCustomRequest(int requestCode) { final int index = requestCode; final CharSequence word = mSuggestedWords.getWord(index); - // TODO: change caller path so coordinates are passed through here - mListener.pickSuggestionManually(index, word, NOT_A_TOUCH_COORDINATE, - NOT_A_TOUCH_COORDINATE); + mListener.pickSuggestionManually(index, word); dismissMoreSuggestions(); return true; } @@ -874,7 +872,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen return; final CharSequence word = mSuggestedWords.getWord(index); - mListener.pickSuggestionManually(index, word, mLastX, mLastY); + mListener.pickSuggestionManually(index, word); } @Override diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index cf6f31a0a..384a2b1a4 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -50,6 +50,7 @@ import android.widget.Toast; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; @@ -1062,9 +1063,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" }; public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs, int x, int y) { + final CharSequence cs) { final Object[] values = { - index, cs, x, y + index, cs, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( @@ -1075,10 +1078,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; public static void latinIME_pickSuggestionManually(final String replacedWord, - final int index, CharSequence suggestion, int x, int y) { + final int index, CharSequence suggestion) { final Object[] values = { - scrubDigitsFromString(replacedWord), index, suggestion == null ? null : - scrubDigitsFromString(suggestion.toString()), x, y + scrubDigitsFromString(replacedWord), index, + (suggestion == null ? null : scrubDigitsFromString(suggestion.toString())), + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, @@ -1089,9 +1094,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y" }; public static void latinIME_punctuationSuggestion(final int index, - final CharSequence suggestion, int x, int y) { + final CharSequence suggestion) { final Object[] values = { - index, suggestion, x, y + index, suggestion, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE }; getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index c672d5126..abf2f75b0 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -260,9 +260,7 @@ public class InputTestsBase extends ServiceTestCase { } protected void pickSuggestionManually(final int index, final CharSequence suggestion) { - mLatinIME.pickSuggestionManually(index, suggestion, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); + mLatinIME.pickSuggestionManually(index, suggestion); } // Helper to avoid writing the try{}catch block each time From ac78633be28e8990fc3b3a8de192c80966e746e3 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 21 Aug 2012 14:05:57 +0900 Subject: [PATCH 090/133] Rename and consolidate some constants Change-Id: I7863936cca18b28bf0117afcbcf544a5fd7f185d --- .../inputmethod/keyboard/KeyDetector.java | 6 ++--- .../keyboard/KeyboardActionListener.java | 24 ++++++++----------- .../keyboard/MainKeyboardView.java | 5 ++-- .../keyboard/MoreKeysKeyboardView.java | 4 +++- .../inputmethod/keyboard/ProximityInfo.java | 5 ++-- .../inputmethod/latin/BinaryDictionary.java | 2 +- .../android/inputmethod/latin/Constants.java | 7 ++++++ .../latin/ExpandableDictionary.java | 7 +++--- .../android/inputmethod/latin/LatinIME.java | 19 +++++++-------- .../inputmethod/latin/WordComposer.java | 9 ++----- .../AndroidWordLevelSpellCheckerSession.java | 5 ++-- .../spellcheck/SpellCheckerProximityInfo.java | 4 ++-- .../inputmethod/research/ResearchLogger.java | 12 ++++------ .../inputmethod/latin/InputTestsBase.java | 5 +--- 14 files changed, 53 insertions(+), 61 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index c0e6aa8d7..868c8cab5 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -16,10 +16,10 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.latin.Constants; + public class KeyDetector { - public static final int NOT_A_CODE = -1; - private final int mKeyHysteresisDistanceSquared; private Keyboard mKeyboard; @@ -103,7 +103,7 @@ public class KeyDetector { final StringBuilder sb = new StringBuilder(); boolean addDelimiter = false; for (final int code : codes) { - if (code == NOT_A_CODE) break; + if (code == Constants.NOT_A_CODE) break; if (addDelimiter) sb.append(", "); sb.append(Keyboard.printableCode(code)); addDelimiter = true; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index b1621a55b..5c8f78f5e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; public interface KeyboardActionListener { @@ -44,21 +45,16 @@ public interface KeyboardActionListener { * * @param primaryCode this is the code of the key that was pressed * @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by - * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}. - * If it's called on insertion from the suggestion strip, it should be - * {@link #SUGGESTION_STRIP_COORDINATE}. + * {@link PointerTracker} or so, the value should be + * {@link Constants#NOT_A_COORDINATE}. If it's called on insertion from the + * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}. * @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by - * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}. - * If it's called on insertion from the suggestion strip, it should be - * {@link #SUGGESTION_STRIP_COORDINATE}. + * {@link PointerTracker} or so, the value should be + * {@link Constants#NOT_A_COORDINATE}.If it's called on insertion from the + * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}. */ public void onCodeInput(int primaryCode, int x, int y); - // See {@link Adapter#isInvalidCoordinate(int)}. - public static final int NOT_A_TOUCH_COORDINATE = -1; - public static final int SUGGESTION_STRIP_COORDINATE = -2; - public static final int SPELL_CHECKER_COORDINATE = -3; - /** * Sends a sequence of characters to the listener. * @@ -119,9 +115,9 @@ public interface KeyboardActionListener { // TODO: Remove this method when the vertical correction is removed. public static boolean isInvalidCoordinate(int coordinate) { - // Detect {@link KeyboardActionListener#NOT_A_TOUCH_COORDINATE}, - // {@link KeyboardActionListener#SUGGESTION_STRIP_COORDINATE}, and - // {@link KeyboardActionListener#SPELL_CHECKER_COORDINATE}. + // Detect {@link Constants#NOT_A_COORDINATE}, + // {@link Constants#SUGGESTION_STRIP_COORDINATE}, and + // {@link Constants#SPELL_CHECKER_COORDINATE}. return coordinate < 0; } } diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 9590290ea..f572cb80d 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -608,9 +608,8 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key } private void invokeCodeInput(int primaryCode) { - mKeyboardActionListener.onCodeInput(primaryCode, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); + mKeyboardActionListener.onCodeInput( + primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } private void invokeReleaseKey(int primaryCode) { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 870eff29f..e513a1477 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -25,6 +25,7 @@ import android.widget.PopupWindow; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.R; @@ -50,7 +51,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel public void onCodeInput(int primaryCode, int x, int y) { // Because a more keys keyboard doesn't need proximity characters correction, we don't // send touch event coordinates. - mListener.onCodeInput(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE); + mListener.onCodeInput( + primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index ac0a56ba3..71bf31faa 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -20,6 +20,7 @@ import android.graphics.Rect; import android.text.TextUtils; import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.JniUtils; import java.util.Arrays; @@ -111,7 +112,7 @@ public class ProximityInfo { final Key[] keys = mKeys; final TouchPositionCorrection touchPositionCorrection = mTouchPositionCorrection; final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; - Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE); + Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE); for (int i = 0; i < mGridSize; ++i) { final int proximityCharsLength = gridNeighborKeys[i].length; for (int j = 0; j < proximityCharsLength; ++j) { @@ -234,7 +235,7 @@ public class ProximityInfo { dest[index++] = code; } if (index < destLength) { - dest[index] = KeyDetector.NOT_A_CODE; + dest[index] = Constants.NOT_A_CODE; } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 2d9e0ce2c..f6635847b 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -136,7 +136,7 @@ public class BinaryDictionary extends Dictionary { final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) { if (!isValidDictionary()) return null; - Arrays.fill(mInputCodePoints, WordComposer.NOT_A_CODE); + Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWord) ? null : StringUtils.toCodePointArray(prevWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 1242967ad..d71c0f995 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -128,6 +128,13 @@ public final class Constants { } } + public static final int NOT_A_CODE = -1; + + // See {@link KeyboardActionListener.Adapter#isInvalidCoordinate(int)}. + public static final int NOT_A_COORDINATE = -1; + public static final int SUGGESTION_STRIP_COORDINATE = -2; + public static final int SPELL_CHECKER_COORDINATE = -3; + private Constants() { // This utility class is not publicly instantiable. } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index d101aaf15..11dca3404 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -292,9 +291,9 @@ public class ExpandableDictionary extends Dictionary { mCodes[i] = new int[ProximityInfo.MAX_PROXIMITY_CHARS_SIZE]; } final int x = xCoordinates != null && i < xCoordinates.length ? - xCoordinates[i] : WordComposer.NOT_A_COORDINATE; + xCoordinates[i] : Constants.NOT_A_COORDINATE; final int y = xCoordinates != null && i < yCoordinates.length ? - yCoordinates[i] : WordComposer.NOT_A_COORDINATE; + yCoordinates[i] : Constants.NOT_A_COORDINATE; proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]); } mMaxDepth = mInputLength * 3; @@ -487,7 +486,7 @@ public class ExpandableDictionary extends Dictionary { for (int j = 0; j < alternativesSize; j++) { final int addedAttenuation = (j > 0 ? 1 : 2); final int currentChar = currentChars[j]; - if (currentChar == KeyDetector.NOT_A_CODE) { + if (currentChar == Constants.NOT_A_CODE) { break; } if (currentChar == lowerC || currentChar == c) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 58df15929..d1a542a18 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1326,8 +1326,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen keyX = x; keyY = y; } else { - keyX = NOT_A_TOUCH_COORDINATE; - keyY = NOT_A_TOUCH_COORDINATE; + keyX = Constants.NOT_A_COORDINATE; + keyY = Constants.NOT_A_COORDINATE; } handleCharacter(primaryCode, keyX, keyY, spaceState); } @@ -1619,7 +1619,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { final boolean swapWeakSpace = maybeStripSpace(primaryCode, - spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); + spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x); sendKeyCodePoint(primaryCode); @@ -1649,7 +1649,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); + Constants.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) { @@ -1896,8 +1896,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); return; } @@ -1957,8 +1956,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If the suggestion is not in the dictionary, the hint should be shown. && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true); - Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); + Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { mSuggestionStripView.showAddToDictionaryHint( suggestion, mCurrentSettings.mHintToSaveText); @@ -2086,8 +2085,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.commitText(originallyTypedWord, 1); // Re-insert the separator sendKeyCodePoint(mLastComposedWord.mSeparatorCode); - Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); + Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_revertCommit(originallyTypedWord); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 5606a58e4..40ee7d23a 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -17,7 +17,6 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import java.util.Arrays; @@ -26,10 +25,6 @@ import java.util.Arrays; * A place to store the currently composing word with information such as adjacent key codes as well */ public class WordComposer { - - public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; - public static final int NOT_A_COORDINATE = -1; - private static final int N = BinaryDictionary.MAX_WORD_LENGTH; private int[] mPrimaryKeyCodes; @@ -166,7 +161,7 @@ public class WordComposer { final int codePoint = Character.codePointAt(word, i); // We don't want to override the batch input points that are held in mInputPointers // (See {@link #add(int,int,int)}). - add(codePoint, NOT_A_COORDINATE, NOT_A_COORDINATE); + add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } } @@ -181,7 +176,7 @@ public class WordComposer { add(codePoint, x, y); return; } - add(codePoint, NOT_A_COORDINATE, NOT_A_COORDINATE); + add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } /** diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 317fe7cd3..f4784ff1a 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -24,6 +24,7 @@ import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -225,8 +226,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript( codePoint, mScript); if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) { - composer.add(codePoint, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); + composer.add(codePoint, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } else { composer.add(codePoint, xy & 0xFFFF, xy >> 16); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index bd92d883b..1762e29aa 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -16,14 +16,14 @@ package com.android.inputmethod.latin.spellcheck; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.Constants; import java.util.TreeMap; public class SpellCheckerProximityInfo { /* public for test */ - final public static int NUL = KeyDetector.NOT_A_CODE; + final public static int NUL = Constants.NOT_A_CODE; // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside // native code - this value is passed at creation of the binary object and reused diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 384a2b1a4..845ba9c4c 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -50,11 +50,11 @@ import android.widget.Toast; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.R; @@ -1065,9 +1065,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_pickApplicationSpecifiedCompletion(final int index, final CharSequence cs) { final Object[] values = { - index, cs, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE + index, cs, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( @@ -1082,8 +1080,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { scrubDigitsFromString(replacedWord), index, (suggestion == null ? null : scrubDigitsFromString(suggestion.toString())), - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE }; final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY, @@ -1097,8 +1094,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final CharSequence suggestion) { final Object[] values = { index, suggestion, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, - KeyboardActionListener.SUGGESTION_STRIP_COORDINATE + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE }; getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index abf2f75b0..9feec2be9 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -39,7 +39,6 @@ import android.widget.TextView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; import java.util.HashMap; @@ -222,9 +221,7 @@ public class InputTestsBase extends ServiceTestCase { return; } } - mLatinIME.onCodeInput(codePoint, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); + mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); //mLatinIME.onReleaseKey(codePoint, false); } From adbd9ae105e06287b59379d7f7127d95fd0663f4 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 21 Aug 2012 19:49:25 +0900 Subject: [PATCH 091/133] Tell the shift mode to the word composer. Bug: 6950087 Change-Id: Ifab9d6d075af398c6468d081216488712f8a53fb --- .../keyboard/KeyboardSwitcher.java | 13 ++++++++ .../android/inputmethod/latin/LatinIME.java | 20 ++++++++--- .../android/inputmethod/latin/Suggest.java | 2 +- .../inputmethod/latin/WordComposer.java | 33 ++++++++++++++----- 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 10f651ad1..0232fae4f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -39,6 +39,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.WordComposer; public class KeyboardSwitcher implements KeyboardState.SwitchActions { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); @@ -402,4 +403,16 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { } } } + + public int getManualCapsMode() { + switch (getKeyboard().mId.mElementId) { + case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: + case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: + return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED; + case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: + return WordComposer.CAPS_MODE_MANUAL_SHIFTED; + default: + return WordComposer.CAPS_MODE_OFF; + } + } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index a7896ad52..f5dc54c41 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1093,6 +1093,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mConnection.getCursorCapsMode(inputType); } + // Factor in auto-caps and manual caps and compute the current caps mode. + private int getActualCapsMode() { + final int manual = mKeyboardSwitcher.getManualCapsMode(); + if (manual != WordComposer.CAPS_MODE_OFF) return manual; + final int auto = getCurrentAutoCapsState(); + if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) { + return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED; + } + if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED; + return WordComposer.CAPS_MODE_OFF; + } + private void swapSwapperAndSpace() { CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. @@ -1377,8 +1389,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mConnection.endBatchEdit(); // TODO: Should handle TextUtils.CAP_MODE_CHARACTER. - mWordComposer.setAutoCapitalized( - getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); + mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); } @Override @@ -1613,8 +1624,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.add(primaryCode, keyX, keyY); // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { - mWordComposer.setAutoCapitalized( - getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); + mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); } mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { @@ -2014,7 +2024,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence prevWord = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2); final String secondWord; - if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) { + if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toString().toLowerCase( mSubtypeSwitcher.getCurrentSubtypeLocale()); } else { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index e9314084a..6a4562c49 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -309,7 +309,7 @@ public class Suggest { final ArrayList suggestionsContainer = new ArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); - final boolean isFirstCharCapitalized = wordComposer.isAutoCapitalized(); + final boolean isFirstCharCapitalized = wordComposer.wasAutoCapitalized(); // TODO: Handle the manual temporary shifted mode. // TODO: Should handle TextUtils.CAP_MODE_CHARACTER. final boolean isAllUpperCase = false; diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 5606a58e4..0580d8954 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -32,6 +32,14 @@ public class WordComposer { private static final int N = BinaryDictionary.MAX_WORD_LENGTH; + public static final int CAPS_MODE_OFF = 0; + // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits + // aren't used anywhere in the code + public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1; + public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3; + public static final int CAPS_MODE_AUTO_SHIFTED = 0x5; + public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7; + private int[] mPrimaryKeyCodes; private final InputPointers mInputPointers = new InputPointers(N); private final StringBuilder mTypedWord; @@ -42,7 +50,7 @@ public class WordComposer { // Cache these values for performance private int mCapsCount; private int mDigitsCount; - private boolean mAutoCapitalized; + private int mCapitalizedMode; private int mTrailingSingleQuotesCount; private int mCodePointSize; @@ -68,7 +76,7 @@ public class WordComposer { mCapsCount = source.mCapsCount; mDigitsCount = source.mDigitsCount; mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; - mAutoCapitalized = source.mAutoCapitalized; + mCapitalizedMode = source.mCapitalizedMode; mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount; mIsResumed = source.mIsResumed; mIsBatchMode = source.mIsBatchMode; @@ -280,20 +288,27 @@ public class WordComposer { } /** - * Saves the reason why the word is capitalized - whether it was automatic or - * due to the user hitting shift in the middle of a sentence. - * @param auto whether it was an automatic capitalization due to start of sentence + * Saves the caps mode at the start of composing. + * + * WordComposer needs to know about this for several reasons. The first is, we need to know + * after the fact what the reason was, to register the correct form into the user history + * dictionary: if the word was automatically capitalized, we should insert it in all-lower + * case but if it's a manual pressing of shift, then it should be inserted as is. + * Also, batch input needs to know about the current caps mode to display correctly + * capitalized suggestions. + * @param mode the mode at the time of start */ - public void setAutoCapitalized(boolean auto) { - mAutoCapitalized = auto; + public void setCapitalizedModeAtStartComposingTime(final int mode) { + mCapitalizedMode = mode; } /** * Returns whether the word was automatically capitalized. * @return whether the word was automatically capitalized */ - public boolean isAutoCapitalized() { - return mAutoCapitalized; + public boolean wasAutoCapitalized() { + return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED + || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED; } /** From 1eba97d92fb5caa4f23425837b6680ccc2a23ae8 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 21 Aug 2012 19:57:21 +0900 Subject: [PATCH 092/133] Treat all caps for gesture input. Bug: 6950087 Change-Id: I9f2271780600572c9befa96c7ad98494c554dd2b --- java/src/com/android/inputmethod/latin/Suggest.java | 6 ++---- java/src/com/android/inputmethod/latin/WordComposer.java | 9 ++++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 6a4562c49..c75322691 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -309,10 +309,8 @@ public class Suggest { final ArrayList suggestionsContainer = new ArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); - final boolean isFirstCharCapitalized = wordComposer.wasAutoCapitalized(); - // TODO: Handle the manual temporary shifted mode. - // TODO: Should handle TextUtils.CAP_MODE_CHARACTER. - final boolean isAllUpperCase = false; + final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); + final boolean isAllUpperCase = wordComposer.isAllUpperCase(); if (isFirstCharCapitalized || isAllUpperCase) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 0580d8954..9ca9b7e71 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -270,7 +270,14 @@ public class WordComposer { * @return true if all user typed chars are upper case, false otherwise */ public boolean isAllUpperCase() { - return (mCapsCount > 0) && (mCapsCount == size()); + return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED + || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED + || (mCapsCount > 0) && (mCapsCount == size()); + } + + public boolean wasShiftedNoLock() { + return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED + || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED; } /** From c8d7a68503fb2122f17a90a4c364d07491917840 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Tue, 21 Aug 2012 12:28:51 -0700 Subject: [PATCH 093/133] Import translations. DO NOT MERGE Change-Id: Id2d245dd2623e5a23871d389aa600b367cbbfe77 Auto-generated-cl: translation import --- java/res/values-hi/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 56642c478..57e9ff451 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -72,7 +72,7 @@ "कखग" "?१२३" "१२३" - "रोकें" + "पॉज़ करें" "प्रतीक्षा करें" "ज़ोर से बोली गई पासवर्ड कुंजियां सुनने के लिए हेडसेट प्‍लग इन करें." "वर्तमान पाठ %s है" @@ -116,7 +116,7 @@ "सहेजने के लिए पुन: स्‍पर्श करें" "शब्‍दकोश उपलब्‍ध है" "उपयोगकर्ता फ़ीडबैक सक्षम करें" - "उपयोग के आंकड़े और क्रैश रिपोर्ट Google को स्वचालित रूप से भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें." + "उपयोग के आंकड़े और क्रैश रिपोर्ट Google को अपने आप भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें." "कीबोर्ड थीम" "अंग्रेज़ी (यूके)" "अंग्रेज़ी (यूएस)" From d0c41e0c68f7f5fafcebeba18f5590dc5421134c Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 1 Aug 2012 17:26:45 -0700 Subject: [PATCH 094/133] ResearchLogger log RichInputConnection Move many ResearchLogger data collection points to RichInputConnection. By collecting data here, developers do not have to remember to keep the ResearchLog consistent with calls to the RichInputConnection. In addition, some unnecessary log points were removed, and the ResearchLogger is now independent of LatinImeLogger. multi-project change with Ifbabefc008d32d99f89e8289e504188e7232ebd4 Bug: 6188932 Change-Id: I4a42d963ba952e703d425acd3a9871d5fca1f03a --- .../android/inputmethod/latin/LatinIME.java | 76 ++----- .../latin/RichInputConnection.java | 86 ++++--- .../inputmethod/research/ResearchLogger.java | 211 ++++++++++-------- 3 files changed, 189 insertions(+), 184 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index f2ebc9776..8ff86ad93 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -907,13 +907,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(null); + } return; } @@ -935,6 +935,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1055,9 +1058,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1111,12 +1111,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); + ResearchLogger.latinIME_swapSwapperAndSpace(); } mKeyboardSwitcher.updateShiftState(); } @@ -1133,9 +1130,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_doubleSpaceAutoPeriod(); - } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1199,9 +1193,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } } private void handleLanguageSwitchKey() { @@ -1238,6 +1229,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } return; } @@ -1254,9 +1248,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } } // Implementation of {@link KeyboardActionListener}. @@ -1367,9 +1358,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(text); - } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1467,9 +1455,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1489,9 +1474,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1520,9 +1502,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); - } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1541,14 +1520,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1873,10 +1846,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, - autoCorrection.toString()); - } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1901,12 +1870,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); - } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); + } return; } @@ -1933,10 +1902,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion( - index, completionInfo.getText()); - } return; } @@ -1945,12 +1910,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); - } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); + } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1985,9 +1950,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2055,9 +2017,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2085,9 +2044,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(deleteLength); - } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8b4c17322..41e59e92d 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,7 +55,9 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.beginBatchEdit(); + if (null != mIC) { + mIC.beginBatchEdit(); + } } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -66,7 +68,9 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + if (--mNestLevel == 0 && null != mIC) { + mIC.endBatchEdit(); + } } private void checkBatchEdit() { @@ -79,12 +83,22 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) mIC.finishComposingText(); + if (null != mIC) { + mIC.finishComposingText(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.commitText(text, i); + if (null != mIC) { + mIC.commitText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitText(text, i); + } + } } public int getCursorCapsMode(final int inputType) { @@ -107,37 +121,72 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) mIC.deleteSurroundingText(i, j); + if (null != mIC) { + mIC.deleteSurroundingText(i, j); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_deleteSurroundingText(i, j); + } + } } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.performEditorAction(actionId); + if (null != mIC) { + mIC.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_performEditorAction(actionId); + } + } } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) mIC.sendKeyEvent(keyEvent); + if (null != mIC) { + mIC.sendKeyEvent(keyEvent); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); + } + } } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.setComposingText(text, i); + if (null != mIC) { + mIC.setComposingText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setComposingText(text, i); + } + } } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) mIC.setSelection(from, to); + if (null != mIC) { + mIC.setSelection(from, to); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setSelection(from, to); + } + } } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCorrection(correctionInfo); + if (null != mIC) { + mIC.commitCorrection(correctionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCorrection(correctionInfo); + } + } } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCompletion(completionInfo); + if (null != mIC) { + mIC.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCompletion(completionInfo); + } + } } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -315,9 +364,6 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } @@ -382,13 +428,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } return true; } @@ -409,13 +449,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); - } return true; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 845ba9c4c..f62241ceb 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,12 +37,14 @@ import android.os.IBinder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -847,20 +849,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -915,47 +903,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { - final Object[] values = { - length - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" }; @@ -980,6 +927,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { + // Capture the TextView contents. This will trigger onUpdateSelection(), so we + // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, + // it can tell that it was generated by the logging code, and not by the user, and + // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1049,29 +1000,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs) { - final Object[] values = { - index, cs, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1099,21 +1027,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1124,12 +1037,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { + "LatinIMESwapSwapperAndSpace" }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpace() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1248,6 +1160,109 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { + "RichInputConnectionCommitCompletion", "completionInfo" + }; + public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { + final Object[] values = { + completionInfo + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { + "RichInputConnectionCommitCorrection", "CorrectionInfo" + }; + public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + final String typedWord = correctionInfo.getOldText().toString(); + final String autoCorrection = correctionInfo.getNewText().toString(); + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { + "RichInputConnectionCommitText", "typedWord", "newCursorPosition" + }; + public static void richInputConnection_commitText(final CharSequence typedWord, + final int newCursorPosition) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord, newCursorPosition + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, + values); + researchLogger.onWordComplete(scrubbedWord); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { + "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" + }; + public static void richInputConnection_deleteSurroundingText(final int beforeLength, + final int afterLength) { + final Object[] values = { + beforeLength, afterLength + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { + "RichInputConnectionFinishComposingText" + }; + public static void richInputConnection_finishComposingText() { + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { + "RichInputConnectionPerformEditorAction", "imeActionNext" + }; + public static void richInputConnection_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { + "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" + }; + public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { + final Object[] values = { + keyEvent.getEventTime(), + keyEvent.getAction(), + keyEvent.getKeyCode() + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { + "RichInputConnectionSetComposingText", "text", "newCursorPosition" + }; + public static void richInputConnection_setComposingText(final CharSequence text, + final int newCursorPosition) { + final Object[] values = { + text, newCursorPosition + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { + "RichInputConnectionSetSelection", "from", "to" + }; + public static void richInputConnection_setSelection(final int from, final int to) { + final Object[] values = { + from, to + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; From 97d3dd58284d1f490cca21cf36a3188f5e5d5f74 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 21 Aug 2012 17:06:57 -0700 Subject: [PATCH 095/133] Revert "ResearchLogger log RichInputConnection" This reverts commit d0c41e0c68f7f5fafcebeba18f5590dc5421134c Change-Id: I5fd8a67a9fdadb7e6532cafecd00c8f33a00ec72 --- .../android/inputmethod/latin/LatinIME.java | 76 +++++-- .../latin/RichInputConnection.java | 86 +++---- .../inputmethod/research/ResearchLogger.java | 211 ++++++++---------- 3 files changed, 184 insertions(+), 189 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8ff86ad93..f2ebc9776 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -907,13 +907,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(null); - } return; } @@ -935,9 +935,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1058,6 +1055,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(typedWord); + } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1111,9 +1111,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpace(); + ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); } mKeyboardSwitcher.updateShiftState(); } @@ -1130,6 +1133,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_doubleSpaceAutoPeriod(); + } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1193,6 +1199,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_performEditorAction(actionId); + } } private void handleLanguageSwitchKey() { @@ -1229,9 +1238,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } return; } @@ -1248,6 +1254,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } } // Implementation of {@link KeyboardActionListener}. @@ -1358,6 +1367,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(text); + } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1455,6 +1467,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(length); + } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1474,6 +1489,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1502,6 +1520,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); + } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1520,8 +1541,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1846,6 +1873,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, + autoCorrection.toString()); + } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1870,12 +1901,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - final int primaryCode = suggestion.charAt(0); - onCodeInput(primaryCode, - Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); } + final int primaryCode = suggestion.charAt(0); + onCodeInput(primaryCode, + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); return; } @@ -1902,6 +1933,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickApplicationSpecifiedCompletion( + index, completionInfo.getText()); + } return; } @@ -1910,12 +1945,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - mExpectingUpdateSelection = true; - commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, - LastComposedWord.NOT_A_SEPARATOR); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); } + mExpectingUpdateSelection = true; + commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, + LastComposedWord.NOT_A_SEPARATOR); mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1950,6 +1985,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(chosenWord); + } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2017,6 +2055,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(length); + } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2044,6 +2085,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(deleteLength); + } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 41e59e92d..8b4c17322 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,9 +55,7 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) { - mIC.beginBatchEdit(); - } + if (null != mIC) mIC.beginBatchEdit(); } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -68,9 +66,7 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) { - mIC.endBatchEdit(); - } + if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); } private void checkBatchEdit() { @@ -83,22 +79,12 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) { - mIC.finishComposingText(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_finishComposingText(); - } - } + if (null != mIC) mIC.finishComposingText(); } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) { - mIC.commitText(text, i); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitText(text, i); - } - } + if (null != mIC) mIC.commitText(text, i); } public int getCursorCapsMode(final int inputType) { @@ -121,72 +107,37 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) { - mIC.deleteSurroundingText(i, j); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_deleteSurroundingText(i, j); - } - } + if (null != mIC) mIC.deleteSurroundingText(i, j); } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) { - mIC.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_performEditorAction(actionId); - } - } + if (null != mIC) mIC.performEditorAction(actionId); } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) { - mIC.sendKeyEvent(keyEvent); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); - } - } + if (null != mIC) mIC.sendKeyEvent(keyEvent); } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) { - mIC.setComposingText(text, i); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_setComposingText(text, i); - } - } + if (null != mIC) mIC.setComposingText(text, i); } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) { - mIC.setSelection(from, to); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_setSelection(from, to); - } - } + if (null != mIC) mIC.setSelection(from, to); } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) { - mIC.commitCorrection(correctionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitCorrection(correctionInfo); - } - } + if (null != mIC) mIC.commitCorrection(correctionInfo); } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) { - mIC.commitCompletion(completionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.richInputConnection_commitCompletion(completionInfo); - } - } + if (null != mIC) mIC.commitCompletion(completionInfo); } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -364,6 +315,9 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(1); + } } } @@ -428,7 +382,13 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } commitText(" ", 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); + } return true; } @@ -449,7 +409,13 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_deleteSurroundingText(2); + } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertSwapPunctuation(); + } return true; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index f62241ceb..845ba9c4c 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,14 +37,12 @@ import android.os.IBinder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -849,6 +847,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } + private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { + "LatinIMECommitText", "typedWord" + }; + + public static void latinIME_commitText(final CharSequence typedWord) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); + researchLogger.onWordComplete(scrubbedWord); + } + private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -903,6 +915,47 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } + private static final String[] EVENTKEYS_CORRECTION = { + "LogCorrection", "subgroup", "before", "after", "position" + }; + public static void logCorrection(final String subgroup, final String before, final String after, + final int position) { + final Object[] values = { + subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position + }; + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { + "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" + }; + public static void latinIME_commitCurrentAutoCorrection(final String typedWord, + final String autoCorrection) { + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { + "LatinIMEDeleteSurroundingText", "length" + }; + public static void latinIME_deleteSurroundingText(final int length) { + final Object[] values = { + length + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { + "LatinIMEDoubleSpaceAutoPeriod" + }; + public static void latinIME_doubleSpaceAutoPeriod() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); + } + private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" }; @@ -927,10 +980,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { - // Capture the TextView contents. This will trigger onUpdateSelection(), so we - // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, - // it can tell that it was generated by the logging code, and not by the user, and - // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1000,6 +1049,29 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } + private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { + "LatinIMEPerformEditorAction", "imeActionNext" + }; + public static void latinIME_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { + "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" + }; + public static void latinIME_pickApplicationSpecifiedCompletion(final int index, + final CharSequence cs) { + final Object[] values = { + index, cs, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); + } + private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1027,6 +1099,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } + private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { + "LatinIMERevertDoubleSpaceWhileInBatchEdit" + }; + public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { + "LatinIMERevertSwapPunctuation" + }; + public static void latinIME_revertSwapPunctuation() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); + } + private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1037,11 +1124,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { - "LatinIMESwapSwapperAndSpace" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { + "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" }; - public static void latinIME_swapSwapperAndSpace() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, + EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1160,109 +1248,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { - "RichInputConnectionCommitCompletion", "completionInfo" - }; - public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { - final Object[] values = { - completionInfo - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { - "RichInputConnectionCommitCorrection", "CorrectionInfo" - }; - public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { - final String typedWord = correctionInfo.getOldText().toString(); - final String autoCorrection = correctionInfo.getNewText().toString(); - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { - "RichInputConnectionCommitText", "typedWord", "newCursorPosition" - }; - public static void richInputConnection_commitText(final CharSequence typedWord, - final int newCursorPosition) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord, newCursorPosition - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, - values); - researchLogger.onWordComplete(scrubbedWord); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { - "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" - }; - public static void richInputConnection_deleteSurroundingText(final int beforeLength, - final int afterLength) { - final Object[] values = { - beforeLength, afterLength - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { - "RichInputConnectionFinishComposingText" - }; - public static void richInputConnection_finishComposingText() { - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { - "RichInputConnectionPerformEditorAction", "imeActionNext" - }; - public static void richInputConnection_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { - "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" - }; - public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { - final Object[] values = { - keyEvent.getEventTime(), - keyEvent.getAction(), - keyEvent.getKeyCode() - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { - "RichInputConnectionSetComposingText", "text", "newCursorPosition" - }; - public static void richInputConnection_setComposingText(final CharSequence text, - final int newCursorPosition) { - final Object[] values = { - text, newCursorPosition - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); - } - - private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { - "RichInputConnectionSetSelection", "from", "to" - }; - public static void richInputConnection_setSelection(final int from, final int to) { - final Object[] values = { - from, to - }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); - } - private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; From 94e7f4bef970431f509a806d1b92b19fc3b5ce7d Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 1 Aug 2012 17:26:45 -0700 Subject: [PATCH 096/133] ResearchLogger log RichInputConnection Move many ResearchLogger data collection points to RichInputConnection. By collecting data here, developers do not have to remember to keep the ResearchLog consistent with calls to the RichInputConnection. In addition, some unnecessary log points were removed, and the ResearchLogger is now independent of LatinImeLogger. multi-project change with I05496cfd762e9a41c42c50099143f1efc0a3d8a2 Bug: 6188932 Change-Id: I424abb2e437901262a9620255493916b7c3ff74b --- .../android/inputmethod/latin/LatinIME.java | 76 ++----- .../latin/RichInputConnection.java | 86 ++++--- .../inputmethod/research/ResearchLogger.java | 211 ++++++++++-------- 3 files changed, 189 insertions(+), 184 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index f2ebc9776..8ff86ad93 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -907,13 +907,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return; mApplicationSpecifiedCompletions = applicationSpecifiedCompletions; if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(null); + } return; } @@ -935,6 +935,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // this case? This says to keep whatever the user typed. mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); setSuggestionStripShown(true); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); + } } private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { @@ -1055,9 +1058,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { mConnection.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), @@ -1111,12 +1111,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Keyboard.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); + ResearchLogger.latinIME_swapSwapperAndSpace(); } mKeyboardSwitcher.updateShiftState(); } @@ -1133,9 +1130,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelDoubleSpacesTimer(); mConnection.deleteSurroundingText(2, 0); mConnection.commitText(". ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_doubleSpaceAutoPeriod(); - } mKeyboardSwitcher.updateShiftState(); return true; } @@ -1199,9 +1193,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void performEditorAction(int actionId) { mConnection.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } } private void handleLanguageSwitchKey() { @@ -1238,6 +1229,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (code >= '0' && code <= '9') { super.sendKeyChar((char)code); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); + } return; } @@ -1254,9 +1248,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String text = new String(new int[] { code }, 0, 1); mConnection.commitText(text, text.length()); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } } // Implementation of {@link KeyboardActionListener}. @@ -1367,9 +1358,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(Keyboard.CODE_SPACE); } mConnection.commitText(text, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(text); - } mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); @@ -1467,9 +1455,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // like the smiley key or the .com key. final int length = mEnteredText.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be // reverting any autocorrect at this point. So we can safely return. @@ -1489,9 +1474,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } else { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } else { if (mLastComposedWord.canRevertCommit()) { @@ -1520,9 +1502,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); - } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -1541,14 +1520,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { mConnection.deleteSurroundingText(1, 0); } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } if (mDeleteCount > DELETE_ACCELERATE_AT) { mConnection.deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { @@ -1873,10 +1846,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "is empty? Impossible! I must commit suicide."); } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord, - autoCorrection.toString()); - } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); @@ -1901,12 +1870,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); - } final int primaryCode = suggestion.charAt(0); onCodeInput(primaryCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_punctuationSuggestion(index, suggestion); + } return; } @@ -1933,10 +1902,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion( - index, completionInfo.getText()); - } return; } @@ -1945,12 +1910,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String replacedWord = mWordComposer.getTypedWord().toString(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion.toString(), index, suggestedWords); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); - } mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion); + } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -1985,9 +1950,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions(); mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } // Add the word to the user history dictionary final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2055,9 +2017,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(length); - } mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestionStrip(); } @@ -2085,9 +2044,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } mConnection.deleteSurroundingText(deleteLength, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(deleteLength); - } if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8b4c17322..41e59e92d 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -55,7 +55,9 @@ public class RichInputConnection { public void beginBatchEdit() { if (++mNestLevel == 1) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.beginBatchEdit(); + if (null != mIC) { + mIC.beginBatchEdit(); + } } else { if (DBG) { throw new RuntimeException("Nest level too deep"); @@ -66,7 +68,9 @@ public class RichInputConnection { } public void endBatchEdit() { if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead - if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + if (--mNestLevel == 0 && null != mIC) { + mIC.endBatchEdit(); + } } private void checkBatchEdit() { @@ -79,12 +83,22 @@ public class RichInputConnection { public void finishComposingText() { checkBatchEdit(); - if (null != mIC) mIC.finishComposingText(); + if (null != mIC) { + mIC.finishComposingText(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } } public void commitText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.commitText(text, i); + if (null != mIC) { + mIC.commitText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitText(text, i); + } + } } public int getCursorCapsMode(final int inputType) { @@ -107,37 +121,72 @@ public class RichInputConnection { public void deleteSurroundingText(final int i, final int j) { checkBatchEdit(); - if (null != mIC) mIC.deleteSurroundingText(i, j); + if (null != mIC) { + mIC.deleteSurroundingText(i, j); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_deleteSurroundingText(i, j); + } + } } public void performEditorAction(final int actionId) { mIC = mParent.getCurrentInputConnection(); - if (null != mIC) mIC.performEditorAction(actionId); + if (null != mIC) { + mIC.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_performEditorAction(actionId); + } + } } public void sendKeyEvent(final KeyEvent keyEvent) { checkBatchEdit(); - if (null != mIC) mIC.sendKeyEvent(keyEvent); + if (null != mIC) { + mIC.sendKeyEvent(keyEvent); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); + } + } } public void setComposingText(final CharSequence text, final int i) { checkBatchEdit(); - if (null != mIC) mIC.setComposingText(text, i); + if (null != mIC) { + mIC.setComposingText(text, i); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setComposingText(text, i); + } + } } public void setSelection(final int from, final int to) { checkBatchEdit(); - if (null != mIC) mIC.setSelection(from, to); + if (null != mIC) { + mIC.setSelection(from, to); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_setSelection(from, to); + } + } } public void commitCorrection(final CorrectionInfo correctionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCorrection(correctionInfo); + if (null != mIC) { + mIC.commitCorrection(correctionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCorrection(correctionInfo); + } + } } public void commitCompletion(final CompletionInfo completionInfo) { checkBatchEdit(); - if (null != mIC) mIC.commitCompletion(completionInfo); + if (null != mIC) { + mIC.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.richInputConnection_commitCompletion(completionInfo); + } + } } public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) { @@ -315,9 +364,6 @@ public class RichInputConnection { if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { deleteSurroundingText(1, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(1); - } } } @@ -382,13 +428,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" ", 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); - } return true; } @@ -409,13 +449,7 @@ public class RichInputConnection { return false; } deleteSurroundingText(2, 0); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_deleteSurroundingText(2); - } commitText(" " + textBeforeCursor.subSequence(0, 1), 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertSwapPunctuation(); - } return true; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 845ba9c4c..f62241ceb 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -37,12 +37,14 @@ import android.os.IBinder; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.Button; @@ -847,20 +849,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang stop(); } - private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = { - "LatinIMECommitText", "typedWord" - }; - - public static void latinIME_commitText(final CharSequence typedWord) { - final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); - final Object[] values = { - scrubbedWord - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values); - researchLogger.onWordComplete(scrubbedWord); - } - private static final String[] EVENTKEYS_USER_FEEDBACK = { "UserFeedback", "FeedbackContents" }; @@ -915,47 +903,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); } - private static final String[] EVENTKEYS_CORRECTION = { - "LogCorrection", "subgroup", "before", "after", "position" - }; - public static void logCorrection(final String subgroup, final String before, final String after, - final int position) { - final Object[] values = { - subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position - }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = { - "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection" - }; - public static void latinIME_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection) { - final Object[] values = { - scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = { - "LatinIMEDeleteSurroundingText", "length" - }; - public static void latinIME_deleteSurroundingText(final int length) { - final Object[] values = { - length - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values); - } - - private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = { - "LatinIMEDoubleSpaceAutoPeriod" - }; - public static void latinIME_doubleSpaceAutoPeriod() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions" }; @@ -980,6 +927,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onWindowHidden(final int savedSelectionStart, final int savedSelectionEnd, final InputConnection ic) { if (ic != null) { + // Capture the TextView contents. This will trigger onUpdateSelection(), so we + // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, + // it can tell that it was generated by the logging code, and not by the user, and + // therefore keep user-visible state as is. ic.beginBatchEdit(); ic.performContextMenuAction(android.R.id.selectAll); CharSequence charSequence = ic.getSelectedText(0); @@ -1049,29 +1000,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values); } - private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = { - "LatinIMEPerformEditorAction", "imeActionNext" - }; - public static void latinIME_performEditorAction(final int imeActionNext) { - final Object[] values = { - imeActionNext - }; - getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values); - } - - private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = { - "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y" - }; - public static void latinIME_pickApplicationSpecifiedCompletion(final int index, - final CharSequence cs) { - final Object[] values = { - index, cs, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE - }; - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueuePotentiallyPrivateEvent( - EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values); - } - private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = { "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y" }; @@ -1099,21 +1027,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values); } - private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = { - "LatinIMERevertDoubleSpaceWhileInBatchEdit" - }; - public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); - } - - private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = { - "LatinIMERevertSwapPunctuation" - }; - public static void latinIME_revertSwapPunctuation() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = { "LatinIMESendKeyCodePoint", "code" }; @@ -1124,12 +1037,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); } - private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = { - "LatinIMESwapSwapperAndSpaceWhileInBatchEdit" + private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { + "LatinIMESwapSwapperAndSpace" }; - public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT, - EVENTKEYS_NULLVALUES); + public static void latinIME_swapSwapperAndSpace() { + getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES); } private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = { @@ -1248,6 +1160,109 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values); } + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = { + "RichInputConnectionCommitCompletion", "completionInfo" + }; + public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { + final Object[] values = { + completionInfo + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { + "RichInputConnectionCommitCorrection", "CorrectionInfo" + }; + public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + final String typedWord = correctionInfo.getOldText().toString(); + final String autoCorrection = correctionInfo.getNewText().toString(); + final Object[] values = { + scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection) + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { + "RichInputConnectionCommitText", "typedWord", "newCursorPosition" + }; + public static void richInputConnection_commitText(final CharSequence typedWord, + final int newCursorPosition) { + final String scrubbedWord = scrubDigitsFromString(typedWord.toString()); + final Object[] values = { + scrubbedWord, newCursorPosition + }; + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT, + values); + researchLogger.onWordComplete(scrubbedWord); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = { + "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength" + }; + public static void richInputConnection_deleteSurroundingText(final int beforeLength, + final int afterLength) { + final Object[] values = { + beforeLength, afterLength + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { + "RichInputConnectionFinishComposingText" + }; + public static void richInputConnection_finishComposingText() { + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT, + EVENTKEYS_NULLVALUES); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = { + "RichInputConnectionPerformEditorAction", "imeActionNext" + }; + public static void richInputConnection_performEditorAction(final int imeActionNext) { + final Object[] values = { + imeActionNext + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = { + "RichInputConnectionSendKeyEvent", "eventTime", "action", "code" + }; + public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { + final Object[] values = { + keyEvent.getEventTime(), + keyEvent.getAction(), + keyEvent.getKeyCode() + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { + "RichInputConnectionSetComposingText", "text", "newCursorPosition" + }; + public static void richInputConnection_setComposingText(final CharSequence text, + final int newCursorPosition) { + final Object[] values = { + text, newCursorPosition + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + } + + private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { + "RichInputConnectionSetSelection", "from", "to" + }; + public static void richInputConnection_setSelection(final int from, final int to) { + final Object[] values = { + from, to + }; + getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + } + private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent" }; From 0a30688080864e59b12196664e7b3cac10d0a8da Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 30 Jul 2012 21:55:42 -0700 Subject: [PATCH 097/133] ResearchLogger to track simple statistics Bug: 6188932 Change-Id: Ie1bb7322706c2d4a56f5e17044bc746e9df1cf18 --- .../inputmethod/research/ResearchLogger.java | 57 ++++++- .../inputmethod/research/Statistics.java | 146 ++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 java/src/com/android/inputmethod/research/Statistics.java diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index f62241ceb..24535ee13 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -34,6 +34,7 @@ import android.graphics.Paint.Style; import android.inputmethodservice.InputMethodService; import android.os.Build; import android.os.IBinder; +import android.os.SystemClock; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; @@ -138,10 +139,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private Dictionary mDictionary; private KeyboardSwitcher mKeyboardSwitcher; private InputMethodService mInputMethodService; + private final Statistics mStatistics; private ResearchLogUploader mResearchLogUploader; private ResearchLogger() { + mStatistics = Statistics.getInstance(); } public static ResearchLogger getInstance() { @@ -271,10 +274,35 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return new File(filesDir, sb.toString()); } + private void checkForEmptyEditor() { + if (mInputMethodService == null) { + return; + } + final InputConnection ic = mInputMethodService.getCurrentInputConnection(); + if (ic == null) { + return; + } + final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(textBefore)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + final CharSequence textAfter = ic.getTextAfterCursor(1, 0); + if (!TextUtils.isEmpty(textAfter)) { + mStatistics.setIsEmptyUponStarting(false); + return; + } + if (textBefore != null && textAfter != null) { + mStatistics.setIsEmptyUponStarting(true); + } + } + private void start() { maybeShowSplashScreen(); updateSuspendedState(); requestIndicatorRedraw(); + mStatistics.reset(); + checkForEmptyEditor(); if (!isAllowedToLog()) { // Log.w(TAG, "not in usability mode; not logging"); return; @@ -298,6 +326,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /* package */ void stop() { + logStatistics(); + publishLogUnit(mCurrentLogUnit, true); + mCurrentLogUnit = new LogUnit(); + if (mMainResearchLog != null) { mMainResearchLog.stop(); } @@ -306,6 +338,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } + private void setLoggingAllowed(boolean enableLogging) { if (mPrefs == null) { return; @@ -706,6 +758,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mLoggingFrequencyState.onWordLogged(); } mCurrentLogUnit = new LogUnit(); + mStatistics.recordWordEntered(); } private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { @@ -900,7 +953,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); + researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java new file mode 100644 index 000000000..4a2cd079c --- /dev/null +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -0,0 +1,146 @@ +/* + * 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.research; + +import com.android.inputmethod.keyboard.Keyboard; + +public class Statistics { + // Number of characters entered during a typing session + int mCharCount; + // Number of letter characters entered during a typing session + int mLetterCount; + // Number of number characters entered + int mNumberCount; + // Number of space characters entered + int mSpaceCount; + // Number of delete operations entered (taps on the backspace key) + int mDeleteKeyCount; + // Number of words entered during a session. + int mWordCount; + // Whether the text field was empty upon editing + boolean mIsEmptyUponStarting; + boolean mIsEmptinessStateKnown; + + // Timers to count average time to enter a key, first press a delete key, + // between delete keys, and then to return typing after a delete key. + final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); + final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); + final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); + + static class AverageTimeCounter { + int mCount; + int mTotalTime; + + public void reset() { + mCount = 0; + mTotalTime = 0; + } + + public void add(long deltaTime) { + mCount++; + mTotalTime += deltaTime; + } + + public int getAverageTime() { + if (mCount == 0) { + return 0; + } + return mTotalTime / mCount; + } + } + + // To account for the interruptions when the user's attention is directed elsewhere, times + // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. + public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds + + // The last time that a tap was performed + private long mLastTapTime; + // The type of the last keypress (delete key or not) + boolean mIsLastKeyDeleteKey; + + private static final Statistics sInstance = new Statistics(); + + public static Statistics getInstance() { + return sInstance; + } + + private Statistics() { + reset(); + } + + public void reset() { + mCharCount = 0; + mLetterCount = 0; + mNumberCount = 0; + mSpaceCount = 0; + mDeleteKeyCount = 0; + mWordCount = 0; + mIsEmptyUponStarting = true; + mIsEmptinessStateKnown = false; + mKeyCounter.reset(); + mBeforeDeleteKeyCounter.reset(); + mDuringRepeatedDeleteKeysCounter.reset(); + mAfterDeleteKeyCounter.reset(); + + mLastTapTime = 0; + mIsLastKeyDeleteKey = false; + } + + public void recordChar(int codePoint, long time) { + final long delta = time - mLastTapTime; + if (codePoint == Keyboard.CODE_DELETE) { + mDeleteKeyCount++; + if (delta < MIN_DELETION_INTERMISSION) { + if (mIsLastKeyDeleteKey) { + mDuringRepeatedDeleteKeysCounter.add(delta); + } else { + mBeforeDeleteKeyCounter.add(delta); + } + } + mIsLastKeyDeleteKey = true; + } else { + mCharCount++; + if (Character.isDigit(codePoint)) { + mNumberCount++; + } + if (Character.isLetter(codePoint)) { + mLetterCount++; + } + if (Character.isSpaceChar(codePoint)) { + mSpaceCount++; + } + if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { + mAfterDeleteKeyCounter.add(delta); + } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { + mKeyCounter.add(delta); + } + mIsLastKeyDeleteKey = false; + } + mLastTapTime = time; + } + + public void recordWordEntered() { + mWordCount++; + } + + public void setIsEmptyUponStarting(final boolean isEmpty) { + mIsEmptyUponStarting = isEmpty; + mIsEmptinessStateKnown = true; + } +} From 5f282ea9e4a4590fcbab6e27d5fca7dacbb40a6a Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 21 Aug 2012 16:34:55 +0900 Subject: [PATCH 098/133] Add CollectionUtils class to create generic collection easily Change-Id: I6b4de9187e122298e5e9cd8ddc9070d062df6a89 --- .../KeyCodeDescriptionMapper.java | 4 +- .../compat/SuggestionSpanUtils.java | 11 +-- .../inputmethod/keyboard/Keyboard.java | 7 +- .../keyboard/KeyboardLayoutSet.java | 9 +- .../inputmethod/keyboard/KeyboardView.java | 3 +- .../inputmethod/keyboard/PointerTracker.java | 3 +- .../keyboard/internal/KeySpecParser.java | 5 +- .../keyboard/internal/KeyStyles.java | 3 +- .../keyboard/internal/KeyboardCodesSet.java | 6 +- .../keyboard/internal/KeyboardIconsSet.java | 3 +- .../keyboard/internal/KeyboardTextsSet.java | 9 +- .../internal/PointerTrackerQueue.java | 4 +- .../inputmethod/latin/AdditionalSubtype.java | 2 +- .../latin/AdditionalSubtypeSettings.java | 4 +- .../inputmethod/latin/BinaryDictionary.java | 2 +- .../latin/BinaryDictionaryFileDumper.java | 4 +- .../latin/BinaryDictionaryGetter.java | 7 +- .../inputmethod/latin/CollectionUtils.java | 89 +++++++++++++++++++ .../latin/DictionaryCollection.java | 10 +-- .../inputmethod/latin/DictionaryFactory.java | 2 +- .../latin/ExpandableBinaryDictionary.java | 8 +- .../latin/ExpandableDictionary.java | 8 +- .../inputmethod/latin/LocaleUtils.java | 2 +- .../inputmethod/latin/SettingsValues.java | 2 +- .../inputmethod/latin/StringUtils.java | 2 +- .../inputmethod/latin/SubtypeLocale.java | 10 +-- .../android/inputmethod/latin/Suggest.java | 10 +-- .../inputmethod/latin/SuggestedWords.java | 10 ++- .../latin/UserHistoryDictionary.java | 4 +- .../UserHistoryDictionaryBigramList.java | 7 +- .../com/android/inputmethod/latin/Utils.java | 6 +- .../AndroidSpellCheckerService.java | 17 ++-- .../AndroidSpellCheckerSession.java | 8 +- .../latin/spellcheck/DictionaryPool.java | 3 +- .../spellcheck/SpellCheckerProximityInfo.java | 5 +- .../suggestions/SuggestionStripView.java | 9 +- .../inputmethod/research/ResearchLogger.java | 13 +-- .../keyboard/SpacebarTextTests.java | 3 +- .../internal/KeySpecParserCsvTests.java | 4 +- .../inputmethod/latin/SubtypeLocaleTests.java | 2 +- .../keyboard/internal/KeyboardTextsSet.tmpl | 9 +- 41 files changed, 215 insertions(+), 114 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/CollectionUtils.java diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 9b74070af..5c45448a5 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -25,6 +25,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -38,7 +39,7 @@ public class KeyCodeDescriptionMapper { private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); // Map of key labels to spoken description resource IDs - private final HashMap mKeyLabelMap; + private final HashMap mKeyLabelMap = CollectionUtils.newHashMap(); // Sparse array of spoken description resource IDs indexed by key codes private final SparseIntArray mKeyCodeMap; @@ -52,7 +53,6 @@ public class KeyCodeDescriptionMapper { } private KeyCodeDescriptionMapper() { - mKeyLabelMap = new HashMap(); mKeyCodeMap = new SparseIntArray(); } diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 1183b5fb9..6ba309fcb 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -16,10 +16,6 @@ package com.android.inputmethod.compat; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; - import android.content.Context; import android.text.Spannable; import android.text.SpannableString; @@ -27,6 +23,11 @@ import android.text.Spanned; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.ArrayList; @@ -119,7 +120,7 @@ public class SuggestionSpanUtils { } else { spannable = new SpannableString(pickedWord); } - final ArrayList suggestionsList = new ArrayList(); + final ArrayList suggestionsList = CollectionUtils.newArrayList(); for (int i = 0; i < suggestedWords.size(); ++i) { if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) { break; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 489cc37ea..a5429e98c 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -33,6 +33,7 @@ import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyboardCodesSet; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.R; @@ -254,9 +255,9 @@ public class Keyboard { public int GRID_WIDTH; public int GRID_HEIGHT; - public final HashSet mKeys = new HashSet(); - public final ArrayList mShiftKeys = new ArrayList(); - public final ArrayList mAltCodeKeysWhileTyping = new ArrayList(); + public final HashSet mKeys = CollectionUtils.newHashSet(); + public final ArrayList mShiftKeys = CollectionUtils.newArrayList(); + public final ArrayList mAltCodeKeysWhileTyping = CollectionUtils.newArrayList(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet(); public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 64b3f0952..0cb8d869f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.InputTypeUtils; import com.android.inputmethod.latin.LatinImeLogger; @@ -71,7 +72,7 @@ public class KeyboardLayoutSet { private final Params mParams; private static final HashMap> sKeyboardCache = - new HashMap>(); + CollectionUtils.newHashMap(); private static final KeysCache sKeysCache = new KeysCache(); public static class KeyboardLayoutSetException extends RuntimeException { @@ -84,11 +85,7 @@ public class KeyboardLayoutSet { } public static class KeysCache { - private final HashMap mMap; - - public KeysCache() { - mMap = new HashMap(); - } + private final HashMap mMap = CollectionUtils.newHashMap(); public void clear() { mMap.clear(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index fe14c3ffc..c8f30e2c9 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -39,6 +39,7 @@ import android.view.ViewGroup; import android.widget.TextView; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -117,7 +118,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { /** True if all keys should be drawn */ private boolean mInvalidateAllKeys; /** The keys that should be drawn */ - private final HashSet mInvalidatedKeys = new HashSet(); + private final HashSet mInvalidatedKeys = CollectionUtils.newHashSet(); /** The working rectangle variable */ private final Rect mWorkingRect = new Rect(); /** The keyboard bitmap buffer for faster updates */ diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 7d565a64f..9d953202e 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -27,6 +27,7 @@ import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.define.ProductionFlag; @@ -125,7 +126,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static int sTouchNoiseThresholdDistanceSquared; private static boolean sNeedsPhantomSuddenMoveEventHack; - private static final ArrayList sTrackers = new ArrayList(); + private static final ArrayList sTrackers = CollectionUtils.newArrayList(); private static final InputPointers sAggregratedPointers = new InputPointers( GestureStroke.DEFAULT_CAPACITY); private static PointerTrackerQueue sPointerTrackerQueue; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 94a7b826f..13214bb9f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -21,6 +21,7 @@ import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; import android.text.TextUtils; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.StringUtils; @@ -258,7 +259,7 @@ public class KeySpecParser { throw new IllegalArgumentException(); } - final ArrayList list = new ArrayList(end - start); + final ArrayList list = CollectionUtils.newArrayList(end - start); for (int i = start; i < end; i++) { list.add(array[i]); } @@ -438,7 +439,7 @@ public class KeySpecParser { // Skip empty entry. if (pos - start > 0) { if (list == null) { - list = new ArrayList(); + list = CollectionUtils.newArrayList(); } list.add(text.substring(start, pos)); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 291b3b943..16fd715df 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -21,6 +21,7 @@ import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.XmlParseUtils; @@ -33,7 +34,7 @@ public class KeyStyles { private static final String TAG = KeyStyles.class.getSimpleName(); private static final boolean DEBUG = false; - final HashMap mStyles = new HashMap(); + final HashMap mStyles = CollectionUtils.newHashMap(); final KeyboardTextsSet mTextsSet; private final KeyStyle mEmptyKeyStyle; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index f7981a320..f7923d0b9 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -17,13 +17,13 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.CollectionUtils; import java.util.HashMap; public class KeyboardCodesSet { - private static final HashMap sLanguageToCodesMap = - new HashMap(); - private static final HashMap sNameToIdMap = new HashMap(); + private static final HashMap sLanguageToCodesMap = CollectionUtils.newHashMap(); + private static final HashMap sNameToIdMap = CollectionUtils.newHashMap(); private int[] mCodes = DEFAULT; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 5155851fe..4a98a3698 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; import android.util.Log; import android.util.SparseIntArray; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -35,7 +36,7 @@ public class KeyboardIconsSet { private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); // Icon name to icon id map. - private static final HashMap sNameToIdsMap = new HashMap(); + private static final HashMap sNameToIdsMap = CollectionUtils.newHashMap(); private static final Object[] NAMES_AND_ATTR_IDS = { "undefined", ATTR_UNDEFINED, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index bec0f1fef..a608cdef0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -45,14 +46,12 @@ import java.util.HashMap; */ public final class KeyboardTextsSet { // Language to texts map. - private static final HashMap sLocaleToTextsMap = - new HashMap(); - private static final HashMap sNameToIdsMap = - new HashMap(); + private static final HashMap sLocaleToTextsMap = CollectionUtils.newHashMap(); + private static final HashMap sNameToIdsMap = CollectionUtils.newHashMap(); private String[] mTexts; // Resource name to text map. - private HashMap mResourceNameToTextsMap = new HashMap(); + private HashMap mResourceNameToTextsMap = CollectionUtils.newHashMap(); public void setLanguage(final String language) { mTexts = sLocaleToTextsMap.get(language); diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 1c7ceaf92..e0858c019 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -18,6 +18,8 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; +import com.android.inputmethod.latin.CollectionUtils; + import java.util.ArrayList; public class PointerTrackerQueue { @@ -32,7 +34,7 @@ public class PointerTrackerQueue { private static final int INITIAL_CAPACITY = 10; private final ArrayList mExpandableArrayOfActivePointers = - new ArrayList(INITIAL_CAPACITY); + CollectionUtils.newArrayList(INITIAL_CAPACITY); private int mArraySize = 0; public synchronized int size() { diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java index f8f1395b3..4b47a261f 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java @@ -91,7 +91,7 @@ public class AdditionalSubtype { } final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); final ArrayList subtypesList = - new ArrayList(prefSubtypeArray.length); + CollectionUtils.newArrayList(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype); if (subtype.getNameResId() == SubtypeLocale.UNKNOWN_KEYBOARD_LAYOUT) { diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java index 779a38823..d01592a4d 100644 --- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java @@ -89,7 +89,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment { super(context, android.R.layout.simple_spinner_item); setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - final TreeSet items = new TreeSet(); + final TreeSet items = CollectionUtils.newTreeSet(); final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context); final int count = imi.getSubtypeCount(); for (int i = 0; i < count; i++) { @@ -533,7 +533,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment { private InputMethodSubtype[] getSubtypes() { final PreferenceGroup group = getPreferenceScreen(); - final ArrayList subtypes = new ArrayList(); + final ArrayList subtypes = CollectionUtils.newArrayList(); final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index f6635847b..c04a99b6a 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -160,7 +160,7 @@ public class BinaryDictionary extends Dictionary { mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes); final int count = Math.min(tmpCount, MAX_PREDICTIONS); - final ArrayList suggestions = new ArrayList(); + final ArrayList suggestions = CollectionUtils.newArrayList(); for (int j = 0; j < count; ++j) { if (composerSize > 0 && mOutputScores[j] < 1) break; final int start = j * MAX_WORD_LENGTH; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 236c198ad..799aea8ef 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -99,7 +99,7 @@ public class BinaryDictionaryFileDumper { } try { - final List list = new ArrayList(); + final List list = CollectionUtils.newArrayList(); do { final String wordListId = c.getString(0); final String wordListLocale = c.getString(1); @@ -267,7 +267,7 @@ public class BinaryDictionaryFileDumper { final ContentResolver resolver = context.getContentResolver(); final List idList = getWordListWordListInfos(locale, context, hasDefaultWordList); - final List fileAddressList = new ArrayList(); + final List fileAddressList = CollectionUtils.newArrayList(); for (WordListInfo id : idList) { final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context); if (null != afd) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index dd11aaa37..4ada909de 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -260,8 +260,7 @@ class BinaryDictionaryGetter { final Context context) { final File[] directoryList = getCachedDirectoryList(context); if (null == directoryList) return EMPTY_FILE_ARRAY; - final HashMap cacheFiles = - new HashMap(); + final HashMap cacheFiles = CollectionUtils.newHashMap(); for (File directory : directoryList) { if (!directory.isDirectory()) continue; final String dirLocale = getWordListIdFromFileName(directory.getName()); @@ -359,7 +358,7 @@ class BinaryDictionaryGetter { } final int formatVersion = raf.readInt(); final int headerSize = raf.readInt(); - final HashMap options = new HashMap(); + final HashMap options = CollectionUtils.newHashMap(); BinaryDictInputOutput.populateOptionsFromFile(raf, headerSize, options); final String version = options.get(VERSION_KEY); if (null == version) { @@ -404,7 +403,7 @@ class BinaryDictionaryGetter { final DictPackSettings dictPackSettings = new DictPackSettings(context); boolean foundMainDict = false; - final ArrayList fileList = new ArrayList(); + final ArrayList fileList = CollectionUtils.newArrayList(); // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = getWordListIdFromFileName(f.getName()); diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java new file mode 100644 index 000000000..789b79a0a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java @@ -0,0 +1,89 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +public class CollectionUtils { + private CollectionUtils() { + // This utility class is not publicly instantiable. + } + + public static HashMap newHashMap() { + return new HashMap(); + } + + public static TreeMap newTreeMap() { + return new TreeMap(); + } + + public static Map newSynchronizedTreeMap() { + final TreeMap treeMap = newTreeMap(); + return Collections.synchronizedMap(treeMap); + } + + public static ConcurrentHashMap newConcurrentHashMap() { + return new ConcurrentHashMap(); + } + + public static HashSet newHashSet() { + return new HashSet(); + } + + public static TreeSet newTreeSet() { + return new TreeSet(); + } + + public static ArrayList newArrayList() { + return new ArrayList(); + } + + public static ArrayList newArrayList(final int initialCapacity) { + return new ArrayList(initialCapacity); + } + + public static ArrayList newArrayList(final Collection collection) { + return new ArrayList(collection); + } + + public static LinkedList newLinkedList() { + return new LinkedList(); + } + + public static CopyOnWriteArrayList newCopyOnWriteArrayList() { + return new CopyOnWriteArrayList(); + } + + public static CopyOnWriteArrayList newCopyOnWriteArrayList( + final Collection collection) { + return new CopyOnWriteArrayList(collection); + } + + public static CopyOnWriteArrayList newCopyOnWriteArrayList(final E[] array) { + return new CopyOnWriteArrayList(array); + } +} diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index ee80f2532..4acab6b05 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -35,22 +35,22 @@ public class DictionaryCollection extends Dictionary { public DictionaryCollection(final String dictType) { super(dictType); - mDictionaries = new CopyOnWriteArrayList(); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); } public DictionaryCollection(final String dictType, Dictionary... dictionaries) { super(dictType); if (null == dictionaries) { - mDictionaries = new CopyOnWriteArrayList(); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); } else { - mDictionaries = new CopyOnWriteArrayList(dictionaries); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } } public DictionaryCollection(final String dictType, Collection dictionaries) { super(dictType); - mDictionaries = new CopyOnWriteArrayList(dictionaries); + mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } @@ -63,7 +63,7 @@ public class DictionaryCollection extends Dictionary { // dictionary and add the rest to it if not null, hence the get(0) ArrayList suggestions = dictionaries.get(0).getSuggestions(composer, prevWord, proximityInfo); - if (null == suggestions) suggestions = new ArrayList(); + if (null == suggestions) suggestions = CollectionUtils.newArrayList(); final int length = dictionaries.size(); for (int i = 1; i < length; ++ i) { final ArrayList sugg = dictionaries.get(i).getSuggestions(composer, diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 06a5f4b72..cdd01d0c7 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -53,7 +53,7 @@ public class DictionaryFactory { createBinaryDictionary(context, locale)); } - final LinkedList dictList = new LinkedList(); + final LinkedList dictList = CollectionUtils.newLinkedList(); final ArrayList assetFileList = BinaryDictionaryGetter.getDictionaryFiles(locale, context); if (null != assetFileList) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 016530abb..cdf5247de 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -62,7 +62,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * that filename. */ private static final HashMap sSharedDictionaryControllers = - new HashMap(); + CollectionUtils.newHashMap(); /** The application context. */ protected final Context mContext; @@ -159,9 +159,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * the native side. */ public void clearFusionDictionary() { + final HashMap attributes = CollectionUtils.newHashMap(); mFusionDictionary = new FusionDictionary(new Node(), - new FusionDictionary.DictionaryOptions(new HashMap(), false, - false)); + new FusionDictionary.DictionaryOptions(attributes, false, false)); } /** @@ -175,7 +175,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mFusionDictionary.add(word, frequency, null); } else { // TODO: Do this in the subclass, with this class taking an arraylist. - final ArrayList shortcutTargets = new ArrayList(); + final ArrayList shortcutTargets = CollectionUtils.newArrayList(); shortcutTargets.add(new WeightedString(shortcutTarget, frequency)); mFusionDictionary.add(word, frequency, shortcutTargets); } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 11dca3404..2aaf95265 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -230,7 +230,7 @@ public class ExpandableDictionary extends Dictionary { childNode.mTerminal = true; if (isShortcutOnly) { if (null == childNode.mShortcutTargets) { - childNode.mShortcutTargets = new ArrayList(); + childNode.mShortcutTargets = CollectionUtils.newArrayList(); } childNode.mShortcutTargets.add(shortcutTarget.toCharArray()); } else { @@ -259,7 +259,7 @@ public class ExpandableDictionary extends Dictionary { return suggestions; } else { if (TextUtils.isEmpty(prevWord)) return null; - final ArrayList suggestions = new ArrayList(); + final ArrayList suggestions = CollectionUtils.newArrayList(); runBigramReverseLookUp(prevWord, suggestions); return suggestions; } @@ -278,7 +278,7 @@ public class ExpandableDictionary extends Dictionary { protected ArrayList getWordsInner(final WordComposer codes, final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) { - final ArrayList suggestions = new ArrayList(); + final ArrayList suggestions = CollectionUtils.newArrayList(); mInputLength = codes.size(); if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; final InputPointers ips = codes.getInputPointers(); @@ -550,7 +550,7 @@ public class ExpandableDictionary extends Dictionary { Node secondWord = searchWord(mRoots, word2, 0, null); LinkedList bigrams = firstWord.mNGrams; if (bigrams == null || bigrams.size() == 0) { - firstWord.mNGrams = new LinkedList(); + firstWord.mNGrams = CollectionUtils.newLinkedList(); bigrams = firstWord.mNGrams; } else { for (NextWord nw : bigrams) { diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java index b938dd336..3b08cab01 100644 --- a/java/src/com/android/inputmethod/latin/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -193,7 +193,7 @@ public class LocaleUtils { } } - private static final HashMap sLocaleCache = new HashMap(); + private static final HashMap sLocaleCache = CollectionUtils.newHashMap(); /** * Creates a locale from a string specification. diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index c8755be9d..dcd2532c1 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -185,7 +185,7 @@ public class SettingsValues { // Helper functions to create member values. private static SuggestedWords createSuggestPuncList(final String[] puncs) { - final ArrayList puncList = new ArrayList(); + final ArrayList puncList = CollectionUtils.newArrayList(); if (puncs != null) { for (final String puncSpec : puncs) { puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec), diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index 6e7d985d6..39c59b44c 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -53,7 +53,7 @@ public class StringUtils { if (TextUtils.isEmpty(csv)) return ""; final String[] elements = csv.split(","); if (!containsInArray(key, elements)) return csv; - final ArrayList result = new ArrayList(elements.length - 1); + final ArrayList result = CollectionUtils.newArrayList(elements.length - 1); for (final String element : elements) { if (!key.equals(element)) result.add(element); } diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java index 21c9c0d1e..de5f515b0 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -45,13 +45,13 @@ public class SubtypeLocale { private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. private static final HashMap sKeyboardLayoutToDisplayNameMap = - new HashMap(); + CollectionUtils.newHashMap(); // Keyboard layout to subtype name resource id map. private static final HashMap sKeyboardLayoutToNameIdsMap = - new HashMap(); + CollectionUtils.newHashMap(); // Exceptional locale to subtype name resource id map. private static final HashMap sExceptionalLocaleToWithLayoutNameIdsMap = - new HashMap(); + CollectionUtils.newHashMap(); private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = "string/subtype_generic_"; private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = @@ -60,11 +60,11 @@ public class SubtypeLocale { "string/subtype_no_language_"; // Exceptional locales to display name map. private static final HashMap sExceptionalDisplayNamesMap = - new HashMap(); + CollectionUtils.newHashMap(); // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap sLocaleAndExtraValueToKeyboardLayoutSetMap = - new HashMap(); + CollectionUtils.newHashMap(); private SubtypeLocale() { // Intentional empty constructor for utility class. diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index c75322691..51ed09604 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -51,7 +51,7 @@ public class Suggest { private Dictionary mMainDictionary; private ContactsBinaryDictionary mContactsDict; private final ConcurrentHashMap mDictionaries = - new ConcurrentHashMap(); + CollectionUtils.newConcurrentHashMap(); public static final int MAX_SUGGESTIONS = 18; @@ -242,7 +242,7 @@ public class Suggest { } final ArrayList suggestionsContainer = - new ArrayList(suggestionsSet); + CollectionUtils.newArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); final boolean isAllUpperCase = wordComposer.isAllUpperCase(); @@ -307,7 +307,7 @@ public class Suggest { } final ArrayList suggestionsContainer = - new ArrayList(suggestionsSet); + CollectionUtils.newArrayList(suggestionsSet); final int suggestionsCount = suggestionsContainer.size(); final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); final boolean isAllUpperCase = wordComposer.isAllUpperCase(); @@ -338,7 +338,7 @@ public class Suggest { typedWordInfo.setDebugString("+"); final int suggestionsSize = suggestions.size(); final ArrayList suggestionsList = - new ArrayList(suggestionsSize); + CollectionUtils.newArrayList(suggestionsSize); suggestionsList.add(typedWordInfo); // Note: i here is the index in mScores[], but the index in mSuggestions is one more // than i because we added the typed word to mSuggestions without touching mScores. @@ -391,7 +391,7 @@ public class Suggest { } public void close() { - final HashSet dictionaries = new HashSet(); + final HashSet dictionaries = CollectionUtils.newHashSet(); dictionaries.addAll(mDictionaries.values()); for (final Dictionary dictionary : dictionaries) { dictionary.close(); diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 88fc006df..68ecfa0d7 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -24,8 +24,10 @@ import java.util.Arrays; import java.util.HashSet; public class SuggestedWords { + private static final ArrayList EMPTY_WORD_INFO_LIST = + CollectionUtils.newArrayList(0); public static final SuggestedWords EMPTY = new SuggestedWords( - new ArrayList(0), false, false, false, false, false); + EMPTY_WORD_INFO_LIST, false, false, false, false, false); public final boolean mTypedWordValid; // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition @@ -83,7 +85,7 @@ public class SuggestedWords { public static ArrayList getFromApplicationSpecifiedCompletions( final CompletionInfo[] infos) { - final ArrayList result = new ArrayList(); + final ArrayList result = CollectionUtils.newArrayList(); for (CompletionInfo info : infos) { if (null != info && info.getText() != null) { result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE, @@ -97,8 +99,8 @@ public class SuggestedWords { // and replace it with what the user currently typed. public static ArrayList getTypedWordAndPreviousSuggestions( final CharSequence typedWord, final SuggestedWords previousSuggestions) { - final ArrayList suggestionsList = new ArrayList(); - final HashSet alreadySeen = new HashSet(); + final ArrayList suggestionsList = CollectionUtils.newArrayList(); + final HashSet alreadySeen = CollectionUtils.newHashSet(); suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED)); alreadySeen.add(typedWord.toString()); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index d516e72ad..6c9d1c250 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -93,10 +93,10 @@ public class UserHistoryDictionary extends ExpandableDictionary { private final static HashMap sDictProjectionMap; private final static ConcurrentHashMap> - sLangDictCache = new ConcurrentHashMap>(); + sLangDictCache = CollectionUtils.newConcurrentHashMap(); static { - sDictProjectionMap = new HashMap(); + 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); diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java index 610652ac1..bb0f5429a 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java @@ -29,9 +29,8 @@ import java.util.Set; public class UserHistoryDictionaryBigramList { public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0; private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName(); - private static final HashMap EMPTY_BIGRAM_MAP = new HashMap(); - private final HashMap> mBigramMap = - new HashMap>(); + private static final HashMap EMPTY_BIGRAM_MAP = CollectionUtils.newHashMap(); + private final HashMap> mBigramMap = CollectionUtils.newHashMap(); private int mSize = 0; public void evictAll() { @@ -57,7 +56,7 @@ public class UserHistoryDictionaryBigramList { if (mBigramMap.containsKey(word1)) { map = mBigramMap.get(word1); } else { - map = new HashMap(); + map = CollectionUtils.newHashMap(); mBigramMap.put(word1, map); } if (!map.containsKey(word2)) { diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index c6b5c338b..9997569a2 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -477,7 +477,7 @@ public class Utils { private static final String HARDWARE_PREFIX = Build.HARDWARE + ","; private static final HashMap sDeviceOverrideValueMap = - new HashMap(); + CollectionUtils.newHashMap(); public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) { final int orientation = res.getConfiguration().orientation; @@ -495,7 +495,7 @@ public class Utils { return sDeviceOverrideValueMap.get(key); } - private static final HashMap EMPTY_LT_HASH_MAP = new HashMap(); + private static final HashMap EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap(); private static final String LOCALE_AND_TIME_STR_SEPARATER = ","; public static HashMap localeAndTimeStrToHashMap(String str) { if (TextUtils.isEmpty(str)) { @@ -506,7 +506,7 @@ public class Utils { if (N < 2 || N % 2 != 0) { return EMPTY_LT_HASH_MAP; } - final HashMap retval = new HashMap(); + final HashMap retval = CollectionUtils.newHashMap(); for (int i = 0; i < N / 2; ++i) { final String localeStr = ss[i * 2]; final long time = Long.valueOf(ss[i * 2 + 1]); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index d05dc021a..eef7a51f2 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -25,6 +25,7 @@ import android.view.textservice.SuggestionsInfo; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.ContactsBinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryCollection; @@ -62,10 +63,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService public static final int CAPITALIZE_ALL = 2; // All caps private final static String[] EMPTY_STRING_ARRAY = new String[0]; - private Map mDictionaryPools = - Collections.synchronizedMap(new TreeMap()); + private Map mDictionaryPools = CollectionUtils.newSynchronizedTreeMap(); private Map mUserDictionaries = - Collections.synchronizedMap(new TreeMap()); + CollectionUtils.newSynchronizedTreeMap(); private ContactsBinaryDictionary mContactsDictionary; // The threshold for a candidate to be offered as a suggestion. @@ -77,7 +77,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService private final Object mUseContactsLock = new Object(); private final HashSet> mDictionaryCollectionsList = - new HashSet>(); + CollectionUtils.newHashSet(); public static final int SCRIPT_LATIN = 0; public static final int SCRIPT_CYRILLIC = 1; @@ -93,7 +93,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService // proximity to pass to the dictionary descent algorithm. // IMPORTANT: this only contains languages - do not write countries in there. // Only the language is searched from the map. - mLanguageToScript = new TreeMap(); + mLanguageToScript = CollectionUtils.newTreeMap(); mLanguageToScript.put("en", SCRIPT_LATIN); mLanguageToScript.put("fr", SCRIPT_LATIN); mLanguageToScript.put("de", SCRIPT_LATIN); @@ -231,7 +231,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService mSuggestionThreshold = suggestionThreshold; mRecommendedThreshold = recommendedThreshold; mMaxLength = maxLength; - mSuggestions = new ArrayList(maxLength + 1); + mSuggestions = CollectionUtils.newArrayList(maxLength + 1); mScores = new int[mMaxLength]; } @@ -359,10 +359,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService private void closeAllDictionaries() { final Map oldPools = mDictionaryPools; - mDictionaryPools = Collections.synchronizedMap(new TreeMap()); + mDictionaryPools = CollectionUtils.newSynchronizedTreeMap(); final Map oldUserDictionaries = mUserDictionaries; - mUserDictionaries = - Collections.synchronizedMap(new TreeMap()); + mUserDictionaries = CollectionUtils.newSynchronizedTreeMap(); new Thread("spellchecker_close_dicts") { @Override public void run() { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index 501a0e221..5a1bd37f5 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -22,6 +22,8 @@ import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; +import com.android.inputmethod.latin.CollectionUtils; + import java.util.ArrayList; public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { @@ -40,10 +42,10 @@ public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSess return null; } final int N = ssi.getSuggestionsCount(); - final ArrayList additionalOffsets = new ArrayList(); - final ArrayList additionalLengths = new ArrayList(); + final ArrayList additionalOffsets = CollectionUtils.newArrayList(); + final ArrayList additionalLengths = CollectionUtils.newArrayList(); final ArrayList additionalSuggestionsInfos = - new ArrayList(); + CollectionUtils.newArrayList(); String currentWord = null; for (int i = 0; i < N; ++i) { final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 218eab7de..53aa6c719 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.spellcheck; import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; @@ -45,7 +46,7 @@ public class DictionaryPool extends LinkedBlockingQueue { private final Locale mLocale; private int mSize; private volatile boolean mClosed; - final static ArrayList noSuggestions = new ArrayList(); + final static ArrayList noSuggestions = CollectionUtils.newArrayList(); private final static DictAndProximity dummyDict = new DictAndProximity( new Dictionary(Dictionary.TYPE_MAIN) { @Override diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index 1762e29aa..fe5225ebd 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.spellcheck; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import java.util.TreeMap; @@ -59,7 +60,7 @@ public class SpellCheckerProximityInfo { // character. // Since we need to build such an array, we want to be able to search in our big proximity // data quickly by character, and a map is probably the best way to do this. - final private static TreeMap INDICES = new TreeMap(); + final private static TreeMap INDICES = CollectionUtils.newTreeMap(); // The proximity here is the union of // - the proximity for a QWERTY keyboard. @@ -122,7 +123,7 @@ public class SpellCheckerProximityInfo { } private static class Cyrillic { - final private static TreeMap INDICES = new TreeMap(); + final private static TreeMap INDICES = CollectionUtils.newTreeMap(); // TODO: The following table is solely based on the keyboard layout. Consult with Russian // speakers on commonly misspelled words/letters. final static int[] PROXIMITY = { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index bd9a0da13..afc4293c0 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -58,6 +58,7 @@ import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -88,9 +89,9 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen private final MoreSuggestions.Builder mMoreSuggestionsBuilder; private final PopupWindow mMoreSuggestionsWindow; - private final ArrayList mWords = new ArrayList(); - private final ArrayList mInfos = new ArrayList(); - private final ArrayList mDividers = new ArrayList(); + private final ArrayList mWords = CollectionUtils.newArrayList(); + private final ArrayList mInfos = CollectionUtils.newArrayList(); + private final ArrayList mDividers = CollectionUtils.newArrayList(); private final PopupWindow mPreviewPopup; private final TextView mPreviewText; @@ -167,7 +168,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen private final int mSuggestionStripOption; - private final ArrayList mTexts = new ArrayList(); + private final ArrayList mTexts = CollectionUtils.newArrayList(); public boolean mMoreSuggestionsAvailable; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index f62241ceb..42b4176d1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -56,6 +56,7 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LatinIME; @@ -114,7 +115,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // the system to do so. /* package */ ResearchLog mIntentionalResearchLog; // LogUnits are queued here and released only when the user requests the intentional log. - private List mIntentionalResearchLogQueue = new ArrayList(); + private List mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -462,7 +463,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void saveLogsForFeedback() { mFeedbackLog = mIntentionalResearchLog; if (mIntentionalResearchLogQueue != null) { - mFeedbackQueue = new ArrayList(mIntentionalResearchLogQueue); + mFeedbackQueue = CollectionUtils.newArrayList(mIntentionalResearchLogQueue); } else { mFeedbackQueue = null; } @@ -472,7 +473,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMainResearchLog = null; mIntentionalResearchLog = null; - mIntentionalResearchLogQueue = new ArrayList(); + mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); } private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; @@ -728,9 +729,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } static class LogUnit { - private final List mKeysList = new ArrayList(); - private final List mValuesList = new ArrayList(); - private final List mIsPotentiallyPrivate = new ArrayList(); + private final List mKeysList = CollectionUtils.newArrayList(); + private final List mValuesList = CollectionUtils.newArrayList(); + private final List mIsPotentiallyPrivate = CollectionUtils.newArrayList(); private void addLogAtom(final String[] keys, final Object[] values, final Boolean isPotentiallyPrivate) { diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java index 87501eeb2..bc5043911 100644 --- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java +++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java @@ -22,6 +22,7 @@ import android.test.AndroidTestCase; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.AdditionalSubtype; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.ImfUtils; import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SubtypeLocale; @@ -31,7 +32,7 @@ import java.util.Locale; public class SpacebarTextTests extends AndroidTestCase { // Locale to subtypes list. - private final ArrayList mSubtypesList = new ArrayList(); + private final ArrayList mSubtypesList = CollectionUtils.newArrayList(); private Resources mRes; diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java index 3dc4543c2..1346c00f4 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java @@ -19,6 +19,8 @@ package com.android.inputmethod.keyboard.internal; import android.app.Instrumentation; import android.test.InstrumentationTestCase; +import com.android.inputmethod.latin.CollectionUtils; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -42,7 +44,7 @@ public class KeySpecParserCsvTests extends InstrumentationTestCase { } private static String[] getAllResourceIdNames(final Class resourceIdClass) { - final ArrayList names = new ArrayList(); + final ArrayList names = CollectionUtils.newArrayList(); for (final Field field : resourceIdClass.getFields()) { if (field.getType() == Integer.TYPE) { names.add(field.getName()); diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java index c70c2fde5..52a3745fa 100644 --- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java +++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java @@ -28,7 +28,7 @@ import java.util.Locale; public class SubtypeLocaleTests extends AndroidTestCase { // Locale to subtypes list. - private final ArrayList mSubtypesList = new ArrayList(); + private final ArrayList mSubtypesList = CollectionUtils.newArrayList(); private Resources mRes; diff --git a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl index f6c84eaf2..774094cd7 100644 --- a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl +++ b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import java.util.HashMap; @@ -45,14 +46,12 @@ import java.util.HashMap; */ public final class KeyboardTextsSet { // Language to texts map. - private static final HashMap sLocaleToTextsMap = - new HashMap(); - private static final HashMap sNameToIdsMap = - new HashMap(); + private static final HashMap sLocaleToTextsMap = CollectionUtils.newHashMap(); + private static final HashMap sNameToIdsMap = CollectionUtils.newHashMap(); private String[] mTexts; // Resource name to text map. - private HashMap mResourceNameToTextsMap = new HashMap(); + private HashMap mResourceNameToTextsMap = CollectionUtils.newHashMap(); public void setLanguage(final String language) { mTexts = sLocaleToTextsMap.get(language); From 32f6503a27fd91ae2e72ab49b0f67dcb0ee76566 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 22 Aug 2012 12:09:24 +0900 Subject: [PATCH 099/133] Fix a silly bug. This would prevent user history to work at all. Bug: 7023638 Change-Id: I5d8867a07fb8859325d274e956ae667c094d0ac1 --- .../src/com/android/inputmethod/latin/ExpandableDictionary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index d101aaf15..89ef284ac 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -251,7 +251,7 @@ public class ExpandableDictionary extends Dictionary { public ArrayList getSuggestions(final WordComposer composer, final CharSequence prevWord, final ProximityInfo proximityInfo) { if (reloadDictionaryIfRequired()) return null; - if (composer.size() <= 1) { + if (composer.size() > 1) { if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) { return null; } From c3f78c9057a5710898feaf8027659484477e5821 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 21 Aug 2012 15:11:02 +0900 Subject: [PATCH 100/133] Remove KeyboardSwitcher reference from ResearchLogger Change-Id: I671a83c19977fda358c8e8a730e22ee155f65512 --- .../keyboard/MainKeyboardView.java | 12 ++++++++- .../android/inputmethod/latin/LatinIME.java | 2 +- .../inputmethod/research/ResearchLogger.java | 25 +++++++++---------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index f572cb80d..1b309a439 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -528,7 +528,17 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key // to properly show the splash screen, which requires that the window token of the // KeyboardView be non-null. if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(); + ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Notify the research logger that the keyboard view has been detached. This is needed + // to invalidate the reference of {@link MainKeyboardView} to null. + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow(); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8ff86ad93..7201042cd 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -361,7 +361,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mPrefs = prefs; LatinImeLogger.init(this, prefs); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().init(this, prefs, mKeyboardSwitcher); + ResearchLogger.getInstance().init(this, prefs); } InputMethodManagerCompatWrapper.init(this); SubtypeSwitcher.init(this); diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 2b2735060..09a22eff1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -54,7 +54,6 @@ import android.widget.Toast; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.CollectionUtils; @@ -138,7 +137,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // used to check whether words are not unique private Suggest mSuggest; private Dictionary mDictionary; - private KeyboardSwitcher mKeyboardSwitcher; + private MainKeyboardView mMainKeyboardView; private InputMethodService mInputMethodService; private final Statistics mStatistics; @@ -152,8 +151,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return sInstance; } - public void init(final InputMethodService ims, final SharedPreferences prefs, - KeyboardSwitcher keyboardSwitcher) { + public void init(final InputMethodService ims, final SharedPreferences prefs) { assert ims != null; if (ims == null) { Log.w(TAG, "IMS is null; logging is off"); @@ -185,7 +183,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); mResearchLogUploader.start(); - mKeyboardSwitcher = keyboardSwitcher; mInputMethodService = ims; mPrefs = prefs; } @@ -199,10 +196,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - public void mainKeyboardView_onAttachedToWindow() { + public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) { + mMainKeyboardView = mainKeyboardView; maybeShowSplashScreen(); } + public void mainKeyboardView_onDetachedFromWindow() { + mMainKeyboardView = null; + } + private boolean hasSeenSplash() { return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false); } @@ -216,7 +218,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mSplashDialog != null && mSplashDialog.isShowing()) { return; } - final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken(); + final IBinder windowToken = mMainKeyboardView != null + ? mMainKeyboardView.getWindowToken() : null; if (windowToken == null) { return; } @@ -593,14 +596,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (!IS_SHOWING_INDICATOR) { return; } - if (mKeyboardSwitcher == null) { + if (mMainKeyboardView == null) { return; } - final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView == null) { - return; - } - mainKeyboardView.invalidateAllKeys(); + mMainKeyboardView.invalidateAllKeys(); } From 1f05cf6dab45c21d1ed334247929434d97c40584 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 22 Aug 2012 12:32:47 +0900 Subject: [PATCH 101/133] Remove GCUtils utility class Change-Id: Ia666aeb10c155d69763faf7f01ceca113b9b4653 --- .../keyboard/KeyboardSwitcher.java | 39 ++++++------------- .../android/inputmethod/latin/LatinIME.java | 13 +------ .../com/android/inputmethod/latin/Utils.java | 38 ------------------ 3 files changed, 13 insertions(+), 77 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 0232fae4f..fd789f029 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -21,7 +21,6 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.util.Log; import android.view.ContextThemeWrapper; -import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; @@ -38,7 +37,6 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WordComposer; public class KeyboardSwitcher implements KeyboardState.SwitchActions { @@ -47,24 +45,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; static class KeyboardTheme { - public final String mName; public final int mThemeId; public final int mStyleId; - public KeyboardTheme(String name, int themeId, int styleId) { - mName = name; + // Note: The themeId should be aligned with "themeId" attribute of Keyboard style + // in values/style.xml. + public KeyboardTheme(int themeId, int styleId) { mThemeId = themeId; mStyleId = styleId; } } private static final KeyboardTheme[] KEYBOARD_THEMES = { - new KeyboardTheme("Basic", 0, R.style.KeyboardTheme), - new KeyboardTheme("HighContrast", 1, R.style.KeyboardTheme_HighContrast), - new KeyboardTheme("Stone", 6, R.style.KeyboardTheme_Stone), - new KeyboardTheme("Stone.Bold", 7, R.style.KeyboardTheme_Stone_Bold), - new KeyboardTheme("GingerBread", 8, R.style.KeyboardTheme_Gingerbread), - new KeyboardTheme("IceCreamSandwich", 5, R.style.KeyboardTheme_IceCreamSandwich), + new KeyboardTheme(0, R.style.KeyboardTheme), + new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast), + new KeyboardTheme(6, R.style.KeyboardTheme_Stone), + new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold), + new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread), + new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich), }; private SubtypeSwitcher mSubtypeSwitcher; @@ -355,22 +353,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { mKeyboardView.closing(); } - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - setContextThemeWrapper(mLatinIME, mKeyboardTheme); - mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( - R.layout.input_view, null); - tryGC = false; - } catch (OutOfMemoryError e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e); - } catch (InflateException e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e); - } - } + setContextThemeWrapper(mLatinIME, mKeyboardTheme); + mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( + R.layout.input_view, null); mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); if (isHardwareAcceleratedDrawingEnabled) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7201042cd..2be568258 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -381,18 +381,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes()); - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - // Shouldn't this be removed? I think that from Honeycomb on, the GC is now actually working - // as expected and this code is useless. - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - initSuggest(); - tryGC = false; - } catch (OutOfMemoryError e) { - tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e); - } - } + initSuggest(); mDisplayOrientation = res.getConfiguration().orientation; diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 9997569a2..fc7a42100 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -65,44 +65,6 @@ public class Utils { } } - public static class GCUtils { - private static final String GC_TAG = GCUtils.class.getSimpleName(); - public static final int GC_TRY_COUNT = 2; - // GC_TRY_LOOP_MAX is used for the hard limit of GC wait, - // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT. - public static final int GC_TRY_LOOP_MAX = 5; - private static final long GC_INTERVAL = DateUtils.SECOND_IN_MILLIS; - private static GCUtils sInstance = new GCUtils(); - private int mGCTryCount = 0; - - public static GCUtils getInstance() { - return sInstance; - } - - public void reset() { - mGCTryCount = 0; - } - - public boolean tryGCOrWait(String metaData, Throwable t) { - if (mGCTryCount == 0) { - System.gc(); - } - if (++mGCTryCount > GC_TRY_COUNT) { - LatinImeLogger.logOnException(metaData, t); - return false; - } else { - try { - Thread.sleep(GC_INTERVAL); - return true; - } catch (InterruptedException e) { - Log.e(GC_TAG, "Sleep was interrupted."); - LatinImeLogger.logOnException(metaData, t); - return false; - } - } - } - } - /* package */ static class RingCharBuffer { private static RingCharBuffer sRingCharBuffer = new RingCharBuffer(); private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; From ecfbf4625c8afd9cde7b79e0c7846b87e20f79e9 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 22 Aug 2012 14:22:20 +0900 Subject: [PATCH 102/133] Add static construct method for SparseArray Change-Id: I6da97f6ea23578318f403bb5d64c30b1663c69ee --- .../accessibility/AccessibilityEntityProvider.java | 3 ++- java/src/com/android/inputmethod/keyboard/Keyboard.java | 2 +- .../com/android/inputmethod/keyboard/KeyboardLayoutSet.java | 2 +- java/src/com/android/inputmethod/keyboard/KeyboardView.java | 4 ++-- .../android/inputmethod/keyboard/internal/KeyStyles.java | 2 +- .../inputmethod/keyboard/internal/PreviewPlacerView.java | 3 ++- .../src/com/android/inputmethod/latin/BinaryDictionary.java | 2 +- java/src/com/android/inputmethod/latin/CollectionUtils.java | 6 ++++++ 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java index 70e38fdb0..039c77b9c 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java @@ -35,6 +35,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.latin.CollectionUtils; /** * Exposes a virtual view sub-tree for {@link KeyboardView} and generates @@ -55,7 +56,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat private final AccessibilityUtils mAccessibilityUtils; /** A map of integer IDs to {@link Key}s. */ - private final SparseArray mVirtualViewIdToKey = new SparseArray(); + private final SparseArray mVirtualViewIdToKey = CollectionUtils.newSparseArray(); /** Temporary rect used to calculate in-screen bounds. */ private final Rect mTempBoundsInScreen = new Rect(); diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index a5429e98c..e37868b3f 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -135,7 +135,7 @@ public class Keyboard { public final Key[] mAltCodeKeysWhileTyping; public final KeyboardIconsSet mIconsSet; - private final SparseArray mKeyCache = new SparseArray(); + private final SparseArray mKeyCache = CollectionUtils.newSparseArray(); private final ProximityInfo mProximityInfo; private final boolean mProximityCharsCorrectionEnabled; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 0cb8d869f..76ac3de22 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -117,7 +117,7 @@ public class KeyboardLayoutSet { int mWidth; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. final SparseArray mKeyboardLayoutSetElementIdToParamsMap = - new SparseArray(); + CollectionUtils.newSparseArray(); static class ElementParams { int mKeyboardXmlId; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index c8f30e2c9..ff428ac85 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -130,9 +130,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private final Paint mPaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); // This sparse array caches key label text height in pixel indexed by key label text size. - private static final SparseArray sTextHeightCache = new SparseArray(); + private static final SparseArray sTextHeightCache = CollectionUtils.newSparseArray(); // This sparse array caches key label text width in pixel indexed by key label text size. - private static final SparseArray sTextWidthCache = new SparseArray(); + private static final SparseArray sTextWidthCache = CollectionUtils.newSparseArray(); private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 16fd715df..e40cf45cc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -91,7 +91,7 @@ public class KeyStyles { private class DeclaredKeyStyle extends KeyStyle { private final String mParentStyleName; - private final SparseArray mStyleAttributes = new SparseArray(); + private final SparseArray mStyleAttributes = CollectionUtils.newSparseArray(); public DeclaredKeyStyle(String parentStyleName) { mParentStyleName = parentStyleName; diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 59a92d625..3f33aee5a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -28,6 +28,7 @@ import android.util.SparseArray; import android.widget.RelativeLayout; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -47,7 +48,7 @@ public class PreviewPlacerView extends RelativeLayout { private int mXOrigin; private int mYOrigin; - private final SparseArray mPointers = new SparseArray(); + private final SparseArray mPointers = CollectionUtils.newSparseArray(); private String mGestureFloatingPreviewText; private int mLastPointerX; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index c04a99b6a..8909526d8 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -63,7 +63,7 @@ public class BinaryDictionary extends Dictionary { private final boolean mUseFullEditDistance; private final SparseArray mDicTraverseSessions = - new SparseArray(); + CollectionUtils.newSparseArray(); // TODO: There should be a way to remove used DicTraverseSession objects from // {@code mDicTraverseSessions}. diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java index 789b79a0a..baa2ee1cd 100644 --- a/java/src/com/android/inputmethod/latin/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import android.util.SparseArray; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -86,4 +88,8 @@ public class CollectionUtils { public static CopyOnWriteArrayList newCopyOnWriteArrayList(final E[] array) { return new CopyOnWriteArrayList(array); } + + public static SparseArray newSparseArray() { + return new SparseArray(); + } } From bf653996eab40e2c66cfd2eaeb48ed5175b78455 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Thu, 2 Aug 2012 20:22:29 -0700 Subject: [PATCH 103/133] ResearchLogging capture full n-gram data - Captures complete motion data for all words in an n-gram. - Also filters n-grams properly; if any word in the n-gram is not in the dictionary, it is not included. - Simplify ResearchLog to not require explicit state - Added LogBuffer class MainLogBuffer class to allow n-gram-level decisions about privacy. - Moved LogUnit out from ResearchLogger multi-project change with Ic70ccb6c2e11eb02d887821434b44daa3eb7a3e2 Bug: 6188932 Change-Id: I731d6cff6539e997874f723b68bdb0d9b651b937 --- java/res/values/strings.xml | 3 +- .../android/inputmethod/latin/LatinIME.java | 8 +- .../research/FeedbackActivity.java | 8 +- .../inputmethod/research/LogBuffer.java | 113 +++++ .../android/inputmethod/research/LogUnit.java | 83 +++ .../inputmethod/research/MainLogBuffer.java | 127 +++++ .../inputmethod/research/ResearchLog.java | 259 ++++------ .../research/ResearchLogUploader.java | 37 +- .../inputmethod/research/ResearchLogger.java | 480 +++++++----------- .../inputmethod/research/Statistics.java | 4 +- 10 files changed, 634 insertions(+), 488 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/LogBuffer.java create mode 100644 java/src/com/android/inputmethod/research/LogUnit.java create mode 100644 java/src/com/android/inputmethod/research/MainLogBuffer.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 07b3f31c7..f2468f5a4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -261,7 +261,8 @@ Send feedback - Include last 5 words entered + + Include last %d words entered Enter your feedback here. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 7201042cd..0729d0f7d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1259,11 +1259,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mLastKeyTime = when; mConnection.beginBatchEdit(); - - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); - } - final KeyboardSwitcher switcher = mKeyboardSwitcher; // The space state depends only on the last character pressed and its own previous // state. Here, we revert the space state to neutral if the key is actually modifying @@ -1345,6 +1340,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord.deactivate(); mEnteredText = null; mConnection.endBatchEdit(); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_onCodeInput(primaryCode, x, y); + } } // Called from PointerTracker through the KeyboardActionListener interface diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java index c9f3b476a..11eae8813 100644 --- a/java/src/com/android/inputmethod/research/FeedbackActivity.java +++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java @@ -18,10 +18,7 @@ package com.android.inputmethod.research; import android.app.Activity; import android.os.Bundle; -import android.text.Editable; -import android.view.View; import android.widget.CheckBox; -import android.widget.EditText; import com.android.inputmethod.latin.R; @@ -31,6 +28,11 @@ public class FeedbackActivity extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.research_feedback_activity); final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout); + final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history); + final CharSequence cs = checkbox.getText(); + final String actualString = String.format(cs.toString(), + ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE); + checkbox.setText(actualString); layout.setActivity(this); } diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java new file mode 100644 index 000000000..ae7b1579a --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogBuffer.java @@ -0,0 +1,113 @@ +/* + * 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.research; + +import com.android.inputmethod.latin.CollectionUtils; + +import java.util.LinkedList; + +/** + * A buffer that holds a fixed number of LogUnits. + * + * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are + * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches + * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to + * stay under the capacity limit. + */ +public class LogBuffer { + protected final LinkedList mLogUnits; + /* package for test */ int mWordCapacity; + // The number of members of mLogUnits that are actual words. + protected int mNumActualWords; + + /** + * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and + * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. + * + * @param wordCapacity maximum number of words + */ + LogBuffer(final int wordCapacity) { + if (wordCapacity <= 0) { + throw new IllegalArgumentException("wordCapacity must be 1 or greater."); + } + mLogUnits = CollectionUtils.newLinkedList(); + mWordCapacity = wordCapacity; + mNumActualWords = 0; + } + + /** + * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's + * (oldest first) if word capacity is reached. + */ + public void shiftIn(LogUnit newLogUnit) { + if (newLogUnit.getWord() == null) { + // This LogUnit isn't a word, so it doesn't count toward the word-limit. + mLogUnits.add(newLogUnit); + return; + } + if (mNumActualWords == mWordCapacity) { + shiftOutThroughFirstWord(); + } + mLogUnits.add(newLogUnit); + mNumActualWords++; // Must be a word, or we wouldn't be here. + } + + private void shiftOutThroughFirstWord() { + while (!mLogUnits.isEmpty()) { + final LogUnit logUnit = mLogUnits.removeFirst(); + onShiftOut(logUnit); + if (logUnit.hasWord()) { + // Successfully shifted out a word-containing LogUnit and made space for the new + // LogUnit. + mNumActualWords--; + break; + } + } + } + + /** + * Removes all LogUnits from the buffer without calling onShiftOut(). + */ + public void clear() { + mLogUnits.clear(); + mNumActualWords = 0; + } + + /** + * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are + * removed in the order entered. This method is not called when shiftOut is called directly. + * + * Base class does nothing; subclasses may override. + */ + protected void onShiftOut(LogUnit logUnit) { + } + + /** + * Called to deliberately remove the oldest LogUnit. Usually called when draining the + * LogBuffer. + */ + public LogUnit shiftOut() { + if (mLogUnits.isEmpty()) { + return null; + } + final LogUnit logUnit = mLogUnits.removeFirst(); + if (logUnit.hasWord()) { + mNumActualWords--; + } + return logUnit; + } +} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java new file mode 100644 index 000000000..d8b3a29ff --- /dev/null +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -0,0 +1,83 @@ +/* + * 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.research; + +import com.android.inputmethod.latin.CollectionUtils; + +import java.util.ArrayList; + +/** + * A group of log statements related to each other. + * + * A LogUnit is collection of LogStatements, each of which is generated by at a particular point + * in the code. (There is no LogStatement class; the data is stored across the instance variables + * here.) A single LogUnit's statements can correspond to all the calls made while in the same + * composing region, or all the calls between committing the last composing region, and the first + * character of the next composing region. + * + * Individual statements in a log may be marked as potentially private. If so, then they are only + * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit + * will not violate the user's privacy. Checks for this may include whether other LogUnits have + * been published recently, or whether the LogUnit contains numbers, etc. + */ +/* package */ class LogUnit { + private final ArrayList mKeysList = CollectionUtils.newArrayList(); + private final ArrayList mValuesList = CollectionUtils.newArrayList(); + private final ArrayList mIsPotentiallyPrivate = CollectionUtils.newArrayList(); + private String mWord; + private boolean mContainsDigit; + + public void addLogStatement(final String[] keys, final Object[] values, + final Boolean isPotentiallyPrivate) { + mKeysList.add(keys); + mValuesList.add(values); + mIsPotentiallyPrivate.add(isPotentiallyPrivate); + } + + public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) { + final int size = mKeysList.size(); + for (int i = 0; i < size; i++) { + if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) { + researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + } + } + } + + public void setWord(String word) { + mWord = word; + } + + public String getWord() { + return mWord; + } + + public boolean hasWord() { + return mWord != null; + } + + public void setContainsDigit() { + mContainsDigit = true; + } + + public boolean hasDigit() { + return mContainsDigit; + } + + public boolean isEmpty() { + return mKeysList.isEmpty(); + } +} diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java new file mode 100644 index 000000000..745768d35 --- /dev/null +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -0,0 +1,127 @@ +/* + * 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.research; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Suggest; + +import java.util.Random; + +public class MainLogBuffer extends LogBuffer { + // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. + private static final int N_GRAM_SIZE = 2; + // The number of words between n-grams to omit from the log. + private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18; + + private final ResearchLog mResearchLog; + private Suggest mSuggest; + + // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if + // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc. + // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a + // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe + // n-gram does appear. + /* package for test */ int mMinWordPeriod; + + // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod + // after a sample is taken. + /* package for test */ int mWordsUntilSafeToSample; + + public MainLogBuffer(final ResearchLog researchLog) { + super(N_GRAM_SIZE); + mResearchLog = researchLog; + mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE; + final Random random = new Random(); + mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod); + } + + public void setSuggest(Suggest suggest) { + mSuggest = suggest; + } + + @Override + public void shiftIn(final LogUnit newLogUnit) { + super.shiftIn(newLogUnit); + if (newLogUnit.hasWord()) { + if (mWordsUntilSafeToSample > 0) { + mWordsUntilSafeToSample--; + } + } + } + + public void resetWordCounter() { + mWordsUntilSafeToSample = mMinWordPeriod; + } + + /** + * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete + * form and still protect the user's privacy. + * + * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any + * non-character data that is typed between words. The decision about privacy is made based on + * the buffer's entire content. If it is decided that the privacy risks are too great to upload + * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g., + * the screen orientation and other characteristics about the device can be uploaded without + * revealing much about the user. + */ + public boolean isSafeToLog() { + // Check that we are not sampling too frequently. Having sampled recently might disclose + // too much of the user's intended meaning. + if (mWordsUntilSafeToSample > 0) { + return false; + } + if (mSuggest == null || !mSuggest.hasMainDictionary()) { + // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word + // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to + // potentially pose a privacy risk. + return false; + } + // Reload the dictionary in case it has changed (e.g., because the user has changed + // languages). + final Dictionary dictionary = mSuggest.getMainDictionary(); + if (dictionary == null) { + return false; + } + // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the + // complete buffer contents in detail. + final int length = mLogUnits.size(); + for (int i = 0; i < length; i++) { + final LogUnit logUnit = mLogUnits.get(i); + final String word = logUnit.getWord(); + if (word == null) { + // Digits outside words are a privacy threat. + if (logUnit.hasDigit()) { + return false; + } + } else { + // Words not in the dictionary are a privacy threat. + if (!(dictionary.isValidWord(word))) { + return false; + } + } + } + // All checks have passed; this buffer's content can be safely uploaded. + return true; + } + + @Override + protected void onShiftOut(LogUnit logUnit) { + if (mResearchLog != null) { + mResearchLog.publish(logUnit, false /* isIncludingPrivateData */); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 18bf3c07f..71a6d6a78 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -26,7 +26,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.research.ResearchLogger.LogUnit; import java.io.BufferedWriter; import java.io.File; @@ -37,6 +36,7 @@ import java.io.OutputStreamWriter; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -51,21 +51,22 @@ import java.util.concurrent.TimeUnit; */ public class ResearchLog { private static final String TAG = ResearchLog.class.getSimpleName(); - private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( - new OutputStreamWriter(new NullOutputStream())); + private static final boolean DEBUG = false; + private static final long FLUSH_DELAY_IN_MS = 1000 * 5; + private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; - final ScheduledExecutorService mExecutor; + /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; private JsonWriter mJsonWriter = NULL_JSON_WRITER; + // true if at least one byte of data has been written out to the log file. This must be + // remembered because JsonWriter requires that calls matching calls to beginObject and + // endObject, as well as beginArray and endArray, and the file is opened lazily, only when + // it is certain that data will be written. Alternatively, the matching call exceptions + // could be caught, but this might suppress other errors. + private boolean mHasWrittenData = false; - private int mLoggingState; - private static final int LOGGING_STATE_UNSTARTED = 0; - private static final int LOGGING_STATE_READY = 1; // don't create file until necessary - private static final int LOGGING_STATE_RUNNING = 2; - private static final int LOGGING_STATE_STOPPING = 3; - private static final int LOGGING_STATE_STOPPED = 4; - private static final long FLUSH_DELAY_IN_MS = 1000 * 5; - + private static final JsonWriter NULL_JSON_WRITER = new JsonWriter( + new OutputStreamWriter(new NullOutputStream())); private static class NullOutputStream extends OutputStream { /** {@inheritDoc} */ @Override @@ -84,128 +85,81 @@ public class ResearchLog { } } - public ResearchLog(File outputFile) { - mExecutor = Executors.newSingleThreadScheduledExecutor(); + public ResearchLog(final File outputFile) { if (outputFile == null) { throw new IllegalArgumentException(); } + mExecutor = Executors.newSingleThreadScheduledExecutor(); mFile = outputFile; - mLoggingState = LOGGING_STATE_UNSTARTED; } - public synchronized void start() throws IOException { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_READY; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - break; - } - } - - public synchronized void stop() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - } finally { - boolean success = mFile.setWritable(false, false); - mLoggingState = LOGGING_STATE_STOPPED; - } - return null; + public synchronized void close() { + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.flush(); + mJsonWriter.close(); + mHasWrittenData = false; } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public boolean isAlive() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - return true; - } - return false; - } - - public void waitUntilStopped(final int timeoutInMs) throws InterruptedException { + } catch (Exception e) { + Log.d(TAG, "error when closing ResearchLog:"); + e.printStackTrace(); + } finally { + if (mFile.exists()) { + mFile.setWritable(false, false); + } + } + return null; + } + }); removeAnyScheduledFlush(); mExecutor.shutdown(); - mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS); } + private boolean mIsAbortSuccessful; + public synchronized void abort() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - mLoggingState = LOGGING_STATE_STOPPED; - isAbortSuccessful = true; - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - try { - mJsonWriter.endArray(); - mJsonWriter.close(); - } finally { - isAbortSuccessful = mFile.delete(); - } - return null; + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + try { + if (mHasWrittenData) { + mJsonWriter.endArray(); + mJsonWriter.close(); + mHasWrittenData = false; } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - mLoggingState = LOGGING_STATE_STOPPING; - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + } finally { + mIsAbortSuccessful = mFile.delete(); + } + return null; + } + }); + removeAnyScheduledFlush(); + mExecutor.shutdown(); } - private boolean isAbortSuccessful; - public boolean isAbortSuccessful() { - return isAbortSuccessful; + public boolean blockingAbort() throws InterruptedException { + abort(); + mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + return mIsAbortSuccessful; + } + + public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException { + mExecutor.awaitTermination(delay, timeUnit); } /* package */ synchronized void flush() { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } + removeAnyScheduledFlush(); + mExecutor.submit(mFlushCallable); } - private Callable mFlushCallable = new Callable() { + private final Callable mFlushCallable = new Callable() { @Override public Object call() throws Exception { - if (mLoggingState == LOGGING_STATE_RUNNING) { - mJsonWriter.flush(); - } + mJsonWriter.flush(); return null; } }; @@ -224,56 +178,40 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } - public synchronized void publishPublicEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - logUnit.publishPublicEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: - } - } - - public synchronized void publishAllEvents(final LogUnit logUnit) { - switch (mLoggingState) { - case LOGGING_STATE_UNSTARTED: - break; - case LOGGING_STATE_READY: - case LOGGING_STATE_RUNNING: - mExecutor.submit(new Callable() { - @Override - public Object call() throws Exception { - logUnit.publishAllEventsTo(ResearchLog.this); - scheduleFlush(); - return null; - } - }); - break; - case LOGGING_STATE_STOPPING: - case LOGGING_STATE_STOPPED: + public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) { + try { + mExecutor.submit(new Callable() { + @Override + public Object call() throws Exception { + logUnit.publishTo(ResearchLog.this, isIncludingPrivateData); + scheduleFlush(); + return null; + } + }); + } catch (RejectedExecutionException e) { + // TODO: Add code to record loss of data, and report. } } private static final String CURRENT_TIME_KEY = "_ct"; private static final String UPTIME_KEY = "_ut"; private static final String EVENT_TYPE_KEY = "_ty"; + void outputEvent(final String[] keys, final Object[] values) { - // not thread safe. + // Not thread safe. + if (keys.length == 0) { + return; + } + if (DEBUG) { + if (keys.length != values.length + 1) { + Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]); + } + } try { if (mJsonWriter == NULL_JSON_WRITER) { mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile))); - mJsonWriter.setLenient(true); mJsonWriter.beginArray(); + mHasWrittenData = true; } mJsonWriter.beginObject(); mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); @@ -283,8 +221,8 @@ public class ResearchLog { for (int i = 0; i < length; i++) { mJsonWriter.name(keys[i + 1]); Object value = values[i]; - if (value instanceof String) { - mJsonWriter.value((String) value); + if (value instanceof CharSequence) { + mJsonWriter.value(value.toString()); } else if (value instanceof Number) { mJsonWriter.value((Number) value); } else if (value instanceof Boolean) { @@ -331,14 +269,11 @@ public class ResearchLog { SuggestedWords words = (SuggestedWords) value; mJsonWriter.beginObject(); mJsonWriter.name("typedWordValid").value(words.mTypedWordValid); - mJsonWriter.name("willAutoCorrect") - .value(words.mWillAutoCorrect); + mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect); mJsonWriter.name("isPunctuationSuggestions") - .value(words.mIsPunctuationSuggestions); - mJsonWriter.name("isObsoleteSuggestions") - .value(words.mIsObsoleteSuggestions); - mJsonWriter.name("isPrediction") - .value(words.mIsPrediction); + .value(words.mIsPunctuationSuggestions); + mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); + mJsonWriter.name("isPrediction").value(words.mIsPrediction); mJsonWriter.name("words"); mJsonWriter.beginArray(); final int size = words.size(); @@ -363,8 +298,8 @@ public class ResearchLog { try { mJsonWriter.close(); } catch (IllegalStateException e1) { - // assume that this is just the json not being terminated properly. - // ignore + // Assume that this is just the json not being terminated properly. + // Ignore } catch (IOException e1) { e1.printStackTrace(); } finally { diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java index 3b1213009..9904a1de2 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java @@ -27,7 +27,6 @@ import android.os.BatteryManager; import android.util.Log; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.R.string; import java.io.BufferedReader; import java.io.File; @@ -48,6 +47,7 @@ public final class ResearchLogUploader { private static final String TAG = ResearchLogUploader.class.getSimpleName(); private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; private final boolean mCanUpload; private final Context mContext; @@ -55,8 +55,6 @@ public final class ResearchLogUploader { private final URL mUrl; private final ScheduledExecutorService mExecutor; - private Runnable doUploadRunnable = new UploadRunnable(null, false); - public ResearchLogUploader(final Context context, final File filesDir) { mContext = context; mFilesDir = filesDir; @@ -93,11 +91,15 @@ public final class ResearchLogUploader { public void start() { if (mCanUpload) { - Log.d(TAG, "scheduling regular uploading"); - mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS, - UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } else { - Log.d(TAG, "no permission to upload"); + mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, + null /* callback */, false /* forceUpload */), + UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); + } + } + + public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { + if (mCanUpload) { + mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); } } @@ -106,7 +108,8 @@ public final class ResearchLogUploader { // another upload happening right now, as it may have missed the latest changes. // TODO: Reschedule regular upload tests starting from now. if (mCanUpload) { - mExecutor.submit(new UploadRunnable(callback, true)); + mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, + true /* forceUpload */)); } } @@ -130,19 +133,33 @@ public final class ResearchLogUploader { } class UploadRunnable implements Runnable { + private final ResearchLog mLogToWaitFor; private final Callback mCallback; private final boolean mForceUpload; - public UploadRunnable(final Callback callback, final boolean forceUpload) { + public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, + final boolean forceUpload) { + mLogToWaitFor = logToWaitFor; mCallback = callback; mForceUpload = forceUpload; } @Override public void run() { + if (mLogToWaitFor != null) { + waitFor(mLogToWaitFor); + } doUpload(); } + private void waitFor(final ResearchLog researchLog) { + try { + researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private void doUpload() { if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { return; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 09a22eff1..45a33eaee 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -68,11 +68,8 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.Locale; import java.util.UUID; @@ -98,24 +95,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); private static final boolean IS_SHOWING_INDICATOR = true; private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false; + public static final int FEEDBACK_WORD_BUFFER_SIZE = 5; // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; - private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user private static final ResearchLogger sInstance = new ResearchLogger(); // to write to a different filename, e.g., for testing, set mFile before calling start() /* package */ File mFilesDir; /* package */ String mUUIDString; /* package */ ResearchLog mMainResearchLog; - // The mIntentionalResearchLog records all events for the session, private or not (excepting + // mFeedbackLog records all events for the session, private or not (excepting // passwords). It is written to permanent storage only if the user explicitly commands // the system to do so. - /* package */ ResearchLog mIntentionalResearchLog; - // LogUnits are queued here and released only when the user requests the intentional log. - private List mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); + // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are + // complete. + /* package */ ResearchLog mFeedbackLog; + /* package */ MainLogBuffer mMainLogBuffer; + /* package */ LogBuffer mFeedbackLogBuffer; private boolean mIsPasswordView = false; private boolean mIsLoggingSuspended = false; @@ -140,9 +139,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private MainKeyboardView mMainKeyboardView; private InputMethodService mInputMethodService; private final Statistics mStatistics; - private ResearchLogUploader mResearchLogUploader; + private LogUnit mCurrentLogUnit = new LogUnit(); + private ResearchLogger() { mStatistics = Statistics.getInstance(); } @@ -269,6 +269,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } + private void setLoggingAllowed(boolean enableLogging) { + if (mPrefs == null) { + return; + } + Editor e = mPrefs.edit(); + e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); + e.apply(); + sIsLogging = enableLogging; + } + private File createLogFile(File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(FILENAME_PREFIX).append('-'); @@ -315,97 +325,58 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); return; } - try { - if (mMainResearchLog == null || !mMainResearchLog.isAlive()) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mMainResearchLog.start(); - if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) { - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - } - mIntentionalResearchLog.start(); - } catch (IOException e) { - Log.w(TAG, "Could not start ResearchLogger."); + if (mMainLogBuffer == null) { + mMainResearchLog = new ResearchLog(createLogFile(mFilesDir)); + mMainLogBuffer = new MainLogBuffer(mMainResearchLog); + mMainLogBuffer.setSuggest(mSuggest); + } + if (mFeedbackLogBuffer == null) { + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); + // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold + // the feedback LogUnit itself. + mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1); } } /* package */ void stop() { logStatistics(); - publishLogUnit(mCurrentLogUnit, true); - mCurrentLogUnit = new LogUnit(); + commitCurrentLogUnit(); - if (mMainResearchLog != null) { - mMainResearchLog.stop(); + if (mMainLogBuffer != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */); + mMainResearchLog.close(); + mMainLogBuffer = null; } - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.stop(); + if (mFeedbackLogBuffer != null) { + mFeedbackLog.close(); + mFeedbackLogBuffer = null; } } - private static final String[] EVENTKEYS_STATISTICS = { - "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", - "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", - "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" - }; - private static void logStatistics() { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - final Object[] values = { - statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, - statistics.mSpaceCount, statistics.mDeleteKeyCount, - statistics.mWordCount, statistics.mIsEmptyUponStarting, - statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), - statistics.mBeforeDeleteKeyCounter.getAverageTime(), - statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), - statistics.mAfterDeleteKeyCounter.getAverageTime() - }; - researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); - } - - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; - } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); - sIsLogging = enableLogging; - } - public boolean abort() { boolean didAbortMainLog = false; - if (mMainResearchLog != null) { - mMainResearchLog.abort(); + if (mMainLogBuffer != null) { + mMainLogBuffer.clear(); try { - mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortMainLog = mMainResearchLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - if (mMainResearchLog.isAbortSuccessful()) { - didAbortMainLog = true; - } - mMainResearchLog = null; + mMainLogBuffer = null; } - boolean didAbortIntentionalLog = false; - if (mIntentionalResearchLog != null) { - mIntentionalResearchLog.abort(); + boolean didAbortFeedbackLog = false; + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.clear(); try { - mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS); + didAbortFeedbackLog = mFeedbackLog.blockingAbort(); } catch (InterruptedException e) { - // interrupted early. carry on. + // Don't know whether this succeeded or not. We assume not; this is reported + // to the caller. } - if (mIntentionalResearchLog.isAbortSuccessful()) { - didAbortIntentionalLog = true; - } - mIntentionalResearchLog = null; - } - return didAbortMainLog && didAbortIntentionalLog; - } - - /* package */ void flush() { - if (mMainResearchLog != null) { - mMainResearchLog.flush(); + mFeedbackLogBuffer = null; } + return didAbortMainLog && didAbortFeedbackLog; } private void restart() { @@ -509,79 +480,39 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class); } - private ResearchLog mFeedbackLog; - private List mFeedbackQueue; - private ResearchLog mSavedMainResearchLog; - private ResearchLog mSavedIntentionalResearchLog; - private List mSavedIntentionalResearchLogQueue; - - private void saveLogsForFeedback() { - mFeedbackLog = mIntentionalResearchLog; - if (mIntentionalResearchLogQueue != null) { - mFeedbackQueue = CollectionUtils.newArrayList(mIntentionalResearchLogQueue); - } else { - mFeedbackQueue = null; - } - mSavedMainResearchLog = mMainResearchLog; - mSavedIntentionalResearchLog = mIntentionalResearchLog; - mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue; - - mMainResearchLog = null; - mIntentionalResearchLog = null; - mIntentionalResearchLogQueue = CollectionUtils.newArrayList(); - } - - private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5; + private static final String[] EVENTKEYS_FEEDBACK = { + "UserTimestamp", "contents" + }; public void sendFeedback(final String feedbackContents, final boolean includeHistory) { - if (includeHistory && mFeedbackLog != null) { - try { - LogUnit headerLogUnit = new LogUnit(); - headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false); - mFeedbackLog.publishAllEvents(headerLogUnit); - for (LogUnit logUnit : mFeedbackQueue) { - mFeedbackLog.publishAllEvents(logUnit); - } - userFeedback(mFeedbackLog, feedbackContents); - mFeedbackLog.stop(); - try { - mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir)); - mIntentionalResearchLog.start(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - mIntentionalResearchLogQueue.clear(); - } - mResearchLogUploader.uploadNow(null); - } else { - // create a separate ResearchLog just for feedback - final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir)); - try { - feedbackLog.start(); - userFeedback(feedbackLog, feedbackContents); - feedbackLog.stop(); - feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS); - mResearchLogUploader.uploadNow(null); - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if (mFeedbackLogBuffer == null) { + return; } + if (!includeHistory) { + mFeedbackLogBuffer.clear(); + } + commitCurrentLogUnit(); + final LogUnit feedbackLogUnit = new LogUnit(); + final Object[] values = { + feedbackContents + }; + feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values, + false /* isPotentiallyPrivate */); + mFeedbackLogBuffer.shiftIn(feedbackLogUnit); + publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); + mFeedbackLog.close(); + mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); + mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; - mMainResearchLog = mSavedMainResearchLog; - mIntentionalResearchLog = mSavedIntentionalResearchLog; - mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue; } public void initSuggest(Suggest suggest) { mSuggest = suggest; + if (mMainLogBuffer != null) { + mMainLogBuffer.setSuggest(mSuggest); + } } private void setIsPasswordView(boolean isPasswordView) { @@ -589,7 +520,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isAllowedToLog() { - return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging; + return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog; } public void requestIndicatorRedraw() { @@ -632,13 +563,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; private static final Object[] EVENTKEYS_NULLVALUES = {}; - private LogUnit mCurrentLogUnit = new LogUnit(); - /** * Buffer a research log event, flagging it as privacy-sensitive. * @@ -654,10 +580,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, true); + mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */); } } + private void setCurrentLogUnitContainsDigitFlag() { + mCurrentLogUnit.setContainsDigit(); + } + /** * Buffer a research log event, flaggint it as not privacy-sensitive. * @@ -673,140 +603,54 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void enqueueEvent(final String[] keys, final Object[] values) { assert values.length + 1 == keys.length; if (isAllowedToLog()) { - mCurrentLogUnit.addLogAtom(keys, values, false); + mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */); } } - // Used to track how often words are logged. Too-frequent logging can leak - // semantics, disclosing private data. - /* package for test */ static class LoggingFrequencyState { - private static final int DEFAULT_WORD_LOG_FREQUENCY = 10; - private int mWordsRemainingToSkip; - private final int mFrequency; - - /** - * Tracks how often words may be uploaded. - * - * @param frequency 1=Every word, 2=Every other word, etc. - */ - public LoggingFrequencyState(int frequency) { - mFrequency = frequency; - mWordsRemainingToSkip = mFrequency; - } - - public void onWordLogged() { - mWordsRemainingToSkip = mFrequency; - } - - public void onWordNotLogged() { - if (mWordsRemainingToSkip > 1) { - mWordsRemainingToSkip--; - } - } - - public boolean isSafeToLog() { - return mWordsRemainingToSkip <= 1; - } - } - - /* package for test */ LoggingFrequencyState mLoggingFrequencyState = - new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY); - - /* package for test */ boolean isPrivacyThreat(String word) { - // Current checks: - // - Word not in dictionary - // - Word contains numbers - // - Privacy-safe word not logged recently - if (TextUtils.isEmpty(word)) { - return false; - } - if (!mLoggingFrequencyState.isSafeToLog()) { - return true; - } - final int length = word.length(); - boolean hasLetter = false; - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(word, i); - if (Character.isDigit(codePoint)) { - return true; - } - if (Character.isLetter(codePoint)) { - hasLetter = true; - break; // Word may contain digits, but will only be allowed if in the dictionary. - } - } - if (hasLetter) { - if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) { - mDictionary = mSuggest.getMainDictionary(); - } - if (mDictionary == null) { - // Can't access dictionary. Assume privacy threat. - return true; - } - return !(mDictionary.isValidWord(word)); - } - // No letters, no numbers. Punctuation, space, or something else. - return false; - } - - private void onWordComplete(String word) { - if (isPrivacyThreat(word)) { - publishLogUnit(mCurrentLogUnit, true); - mLoggingFrequencyState.onWordNotLogged(); - } else { - publishLogUnit(mCurrentLogUnit, false); - mLoggingFrequencyState.onWordLogged(); - } - mCurrentLogUnit = new LogUnit(); - mStatistics.recordWordEntered(); - } - - private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) { - if (!isAllowedToLog()) { - return; - } - if (mMainResearchLog == null) { - return; - } - if (isPrivacySensitive) { - mMainResearchLog.publishPublicEvents(logUnit); - } else { - mMainResearchLog.publishAllEvents(logUnit); - } - mIntentionalResearchLogQueue.add(logUnit); - } - - /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) { - publishLogUnit(mCurrentLogUnit, isPrivacySensitive); - } - - static class LogUnit { - private final List mKeysList = CollectionUtils.newArrayList(); - private final List mValuesList = CollectionUtils.newArrayList(); - private final List mIsPotentiallyPrivate = CollectionUtils.newArrayList(); - - private void addLogAtom(final String[] keys, final Object[] values, - final Boolean isPotentiallyPrivate) { - mKeysList.add(keys); - mValuesList.add(values); - mIsPotentiallyPrivate.add(isPotentiallyPrivate); - } - - public void publishPublicEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - if (!mIsPotentiallyPrivate.get(i)) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + /* package for test */ void commitCurrentLogUnit() { + if (!mCurrentLogUnit.isEmpty()) { + if (mMainLogBuffer != null) { + mMainLogBuffer.shiftIn(mCurrentLogUnit); + if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) { + publishLogBuffer(mMainLogBuffer, mMainResearchLog, + true /* isIncludingPrivateData */); + mMainLogBuffer.resetWordCounter(); } } + if (mFeedbackLogBuffer != null) { + mFeedbackLogBuffer.shiftIn(mCurrentLogUnit); + } + mCurrentLogUnit = new LogUnit(); + Log.d(TAG, "commitCurrentLogUnit"); } + } - public void publishAllEventsTo(ResearchLog researchLog) { - final int size = mKeysList.size(); - for (int i = 0; i < size; i++) { - researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i)); + /* package for test */ void publishLogBuffer(final LogBuffer logBuffer, + final ResearchLog researchLog, final boolean isIncludingPrivateData) { + LogUnit logUnit; + while ((logUnit = logBuffer.shiftOut()) != null) { + researchLog.publish(logUnit, isIncludingPrivateData); + } + } + + private boolean hasOnlyLetters(final String word) { + final int length = word.length(); + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = word.codePointAt(i); + if (!Character.isLetter(codePoint)) { + return false; } } + return true; + } + + private void onWordComplete(final String word) { + Log.d(TAG, "onWordComplete: " + word); + if (word != null && word.length() > 0 && hasOnlyLetters(word)) { + mCurrentLogUnit.setWord(word); + mStatistics.recordWordEntered(); + } + commitCurrentLogUnit(); } private static int scrubDigitFromCodePoint(int codePoint) { @@ -859,12 +703,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return WORD_REPLACEMENT_STRING; } - // Special methods related to startup, shutdown, logging itself - - private static final String[] EVENTKEYS_INTENTIONAL_LOG = { - "IntentionalLog" - }; - private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = { "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions", "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion" @@ -872,9 +710,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs) { final ResearchLogger researchLogger = getInstance(); - if (researchLogger.mInFeedbackDialog) { - researchLogger.saveLogsForFeedback(); - } researchLogger.start(); if (editorInfo != null) { final Context context = researchLogger.mInputMethodService; @@ -906,16 +741,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; - private void userFeedback(ResearchLog researchLog, String feedbackContents) { - // this method is special; it directs the feedbackContents to a particular researchLog - final LogUnit logUnit = new LogUnit(); - final Object[] values = { - feedbackContents - }; - logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false); - researchLog.publishAllEvents(logUnit); - } - // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { @@ -950,12 +775,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "LatinIMEOnCodeInput", "code", "x", "y" }; public static void latinIME_onCodeInput(final int code, final int x, final int y) { + final long time = SystemClock.uptimeMillis(); + final ResearchLogger researchLogger = getInstance(); final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y }; - final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values); - researchLogger.mStatistics.recordChar(code, SystemClock.uptimeMillis()); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } + researchLogger.mStatistics.recordChar(code, time); } private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = { @@ -1020,9 +849,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } final ResearchLogger researchLogger = getInstance(); researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values); - // Play it safe. Remove privacy-sensitive events. - researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true); - researchLogger.mCurrentLogUnit = new LogUnit(); + researchLogger.commitCurrentLogUnit(); getInstance().stop(); } } @@ -1089,7 +916,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { Keyboard.printableCode(scrubDigitFromCodePoint(code)) }; - getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + final ResearchLogger researchLogger = getInstance(); + researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } } private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = { @@ -1227,10 +1058,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values); } + // Disabled for privacy-protection reasons. Because this event comes after + // richInputConnection_commitText, which is the event used to separate LogUnits, the + // data in this event can be associated with the next LogUnit, revealing information + // about the current word even if it was supposed to be suppressed. The occurrance of + // autocorrection can be determined by examining the difference between the text strings in + // the last call to richInputConnection_setComposingText before + // richInputConnection_commitText, so it's not a data loss. + // TODO: Figure out how to log this event without loss of privacy. + /* private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = { - "RichInputConnectionCommitCorrection", "CorrectionInfo" + "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection" }; + */ public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) { + /* final String typedWord = correctionInfo.getOldText().toString(); final String autoCorrection = correctionInfo.getNewText().toString(); final Object[] values = { @@ -1239,6 +1081,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); researchLogger.enqueuePotentiallyPrivateEvent( EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values); + */ } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = { @@ -1264,7 +1107,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { beforeLength, afterLength }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); + getInstance().enqueuePotentiallyPrivateEvent( + EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = { @@ -1294,7 +1138,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang keyEvent.getAction(), keyEvent.getKeyCode() }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT, + values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = { @@ -1302,10 +1147,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang }; public static void richInputConnection_setComposingText(final CharSequence text, final int newCursorPosition) { + if (text == null) { + throw new RuntimeException("setComposingText is null"); + } final Object[] values = { text, newCursorPosition }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, + values); } private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = { @@ -1315,7 +1164,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Object[] values = { from, to }; - getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, values); + getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION, + values); } private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = { @@ -1350,4 +1200,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public void userTimestamp() { getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES); } + + private static final String[] EVENTKEYS_STATISTICS = { + "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount", + "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys", + "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete" + }; + private static void logStatistics() { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + final Object[] values = { + statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount, + statistics.mSpaceCount, statistics.mDeleteKeyCount, + statistics.mWordCount, statistics.mIsEmptyUponStarting, + statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), + statistics.mBeforeDeleteKeyCounter.getAverageTime(), + statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), + statistics.mAfterDeleteKeyCounter.getAverageTime() + }; + researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values); + } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index 4a2cd079c..eab465aa2 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -66,8 +66,8 @@ public class Statistics { // To account for the interruptions when the user's attention is directed elsewhere, times // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final int MIN_TYPING_INTERMISSION = 5 * 1000; // in milliseconds - public static final int MIN_DELETION_INTERMISSION = 15 * 1000; // in milliseconds + public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds + public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds // The last time that a tap was performed private long mLastTapTime; From 9fc6af325ec91a1a22c0679d374c0c3d6d03d9ac Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 22 Aug 2012 10:19:56 +0900 Subject: [PATCH 104/133] Fix SubtypeSwitcher initialization path This change also removes the reference of LatinIME from SubtypeSwitcher. Bug: 7026856 Change-Id: I78a266f3682b11f09b49bbcdcdb6aee713e6af37 --- .../android/inputmethod/latin/LatinIME.java | 31 ++++-- .../inputmethod/latin/SubtypeSwitcher.java | 94 ++++++++++--------- .../inputmethod/latin/InputTestsBase.java | 3 +- 3 files changed, 76 insertions(+), 52 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 2be568258..4d5f93b8f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -405,7 +405,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Has to be package-visible for unit tests - /* package */ void loadSettings() { + /* package for test */ + void loadSettings() { // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this); @@ -529,7 +530,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(Configuration conf) { - mSubtypeSwitcher.onConfigurationChanged(conf); + // System locale has been changed. Needs to reload keyboard. + if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) { + loadKeyboard(); + } // If orientation changed while predicting, commit the change if (mDisplayOrientation != conf.orientation) { mDisplayOrientation = conf.orientation; @@ -596,6 +600,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. mSubtypeSwitcher.updateSubtype(subtype); + loadKeyboard(); } private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) { @@ -664,7 +669,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo); final boolean isDifferentTextField = !restarting || inputTypeChanged; if (isDifferentTextField) { - mSubtypeSwitcher.updateParametersOnStartInputView(); + final boolean currentSubtypeEnabled = mSubtypeSwitcher + .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); + if (!currentSubtypeEnabled) { + // Current subtype is disabled. Needs to update subtype and keyboard. + final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype( + this, mSubtypeSwitcher.getNoLanguageSubtype()); + mSubtypeSwitcher.updateSubtype(newSubtype); + loadKeyboard(); + } } // The EditorInfo might have a flag that affects fullscreen mode. @@ -1285,7 +1298,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen onSettingsKeyPressed(); break; case Keyboard.CODE_SHORTCUT: - mSubtypeSwitcher.switchToShortcutIME(); + mSubtypeSwitcher.switchToShortcutIME(this); break; case Keyboard.CODE_ACTION_ENTER: performEditorAction(getActionId(switcher.getKeyboard())); @@ -1689,7 +1702,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: make this private // Outside LatinIME, only used by the test suite. - /* package for tests */ boolean isShowingPunctuationList() { + /* package for tests */ + boolean isShowingPunctuationList() { if (mSuggestionStripView == null) return false; return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions(); } @@ -2057,9 +2071,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mCurrentSettings.isWordSeparator(code); } - // Notify that language or mode have been changed and toggleLanguage will update KeyboardID - // according to new language or mode. Called from SubtypeSwitcher. - public void onRefreshKeyboard() { + // TODO: Make this private + // Outside LatinIME, only used by the {@link InputTestsBase} test suite. + /* package for test */ + void loadKeyboard() { // When the device locale is changed in SetupWizard etc., this method may get called via // onConfigurationChanged before SoftInputWindow is shown. initSuggest(); diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index a7a5fcb5f..c693edcca 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; +import android.inputmethodservice.InputMethodService; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; @@ -42,7 +43,6 @@ public class SubtypeSwitcher { private static final String TAG = SubtypeSwitcher.class.getSimpleName(); private static final SubtypeSwitcher sInstance = new SubtypeSwitcher(); - private /* final */ LatinIME mService; private /* final */ InputMethodManager mImm; private /* final */ Resources mResources; private /* final */ ConnectivityManager mConnectivityManager; @@ -68,11 +68,11 @@ public class SubtypeSwitcher { return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage; } - public void updateEnabledSubtypeCount(int count) { + public void updateEnabledSubtypeCount(final int count) { mEnabledSubtypeCount = count; } - public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) { + public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) { mIsSystemLanguageSameAsInputLanguage = isSame; } } @@ -81,18 +81,17 @@ public class SubtypeSwitcher { return sInstance; } - public static void init(LatinIME service) { - SubtypeLocale.init(service); - sInstance.initialize(service); - sInstance.updateAllParameters(); + public static void init(final Context context) { + SubtypeLocale.init(context); + sInstance.initialize(context); + sInstance.updateAllParameters(context); } private SubtypeSwitcher() { // Intentional empty constructor for singleton. } - private void initialize(LatinIME service) { - mService = service; + private void initialize(final Context service) { mResources = service.getResources(); mImm = ImfUtils.getInputMethodManager(service); mConnectivityManager = (ConnectivityManager) service.getSystemService( @@ -111,39 +110,46 @@ public class SubtypeSwitcher { // Update all parameters stored in SubtypeSwitcher. // Only configuration changed event is allowed to call this because this is heavy. - private void updateAllParameters() { + private void updateAllParameters(final Context context) { mCurrentSystemLocale = mResources.getConfiguration().locale; - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); - updateParametersOnStartInputView(); + updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype)); + updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled(); } - // Update parameters which are changed outside LatinIME. This parameters affect UI so they - // should be updated every time onStartInputview. - public void updateParametersOnStartInputView() { - updateEnabledSubtypes(); + /** + * Update parameters which are changed outside LatinIME. This parameters affect UI so they + * should be updated every time onStartInputView. + * + * @return true if the current subtype is enabled. + */ + public boolean updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled() { + final boolean currentSubtypeEnabled = + updateEnabledSubtypesAndReturnIfEnabled(mCurrentSubtype); updateShortcutIME(); + return currentSubtypeEnabled; } - // Reload enabledSubtypes from the framework. - private void updateEnabledSubtypes() { - final InputMethodSubtype currentSubtype = mCurrentSubtype; - boolean foundCurrentSubtypeBecameDisabled = true; + /** + * Update enabled subtypes from the framework. + * + * @param subtype the subtype to be checked + * @return true if the {@code subtype} is enabled. + */ + private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) { final List enabledSubtypesOfThisIme = mImm.getEnabledInputMethodSubtypeList(null, true); - for (InputMethodSubtype ims : enabledSubtypesOfThisIme) { - if (ims.equals(currentSubtype)) { - foundCurrentSubtypeBecameDisabled = false; - } - } mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); - if (foundCurrentSubtypeBecameDisabled) { - if (DBG) { - Log.w(TAG, "Last subtype: " - + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue()); - Log.w(TAG, "Last subtype was disabled. Update to the current one."); + + for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) { + if (ims.equals(subtype)) { + return true; } - updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype)); } + if (DBG) { + Log.w(TAG, "Subtype: " + subtype.getLocale() + "/" + subtype.getExtraValue() + + " was disabled"); + } + return false; } private void updateShortcutIME() { @@ -159,8 +165,8 @@ public class SubtypeSwitcher { mImm.getShortcutInputMethodsAndSubtypes(); mShortcutInputMethodInfo = null; mShortcutSubtype = null; - for (InputMethodInfo imi : shortcuts.keySet()) { - List subtypes = shortcuts.get(imi); + for (final InputMethodInfo imi : shortcuts.keySet()) { + final List subtypes = shortcuts.get(imi); // TODO: Returns the first found IMI for now. Should handle all shortcuts as // appropriate. mShortcutInputMethodInfo = imi; @@ -194,24 +200,24 @@ public class SubtypeSwitcher { mCurrentSubtype = newSubtype; updateShortcutIME(); - mService.onRefreshKeyboard(); } //////////////////////////// // Shortcut IME functions // //////////////////////////// - public void switchToShortcutIME() { + public void switchToShortcutIME(final InputMethodService context) { if (mShortcutInputMethodInfo == null) { return; } final String imiId = mShortcutInputMethodInfo.getId(); - switchToTargetIME(imiId, mShortcutSubtype); + switchToTargetIME(imiId, mShortcutSubtype, context); } - private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) { - final IBinder token = mService.getWindow().getWindow().getAttributes().token; + private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype, + final InputMethodService context) { + final IBinder token = context.getWindow().getWindow().getAttributes().token; if (token == null) { return; } @@ -253,7 +259,7 @@ public class SubtypeSwitcher { return true; } - public void onNetworkStateChanged(Intent intent) { + public void onNetworkStateChanged(final Intent intent) { final boolean noConnection = intent.getBooleanExtra( ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); mIsNetworkConnected = !noConnection; @@ -265,7 +271,7 @@ public class SubtypeSwitcher { // Subtype Switching functions // ////////////////////////////////// - public boolean needsToDisplayLanguage(Locale keyboardLocale) { + public boolean needsToDisplayLanguage(final Locale keyboardLocale) { if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) { return true; } @@ -279,12 +285,14 @@ public class SubtypeSwitcher { return SubtypeLocale.getSubtypeLocale(mCurrentSubtype); } - public void onConfigurationChanged(Configuration conf) { + public boolean onConfigurationChanged(final Configuration conf, final Context context) { final Locale systemLocale = conf.locale; + final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale); // If system configuration was changed, update all parameters. - if (!systemLocale.equals(mCurrentSystemLocale)) { - updateAllParameters(); + if (systemLocaleChanged) { + updateAllParameters(context); } + return systemLocaleChanged; } public InputMethodSubtype getCurrentSubtype() { diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 9feec2be9..ffd95f57a 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -135,7 +135,6 @@ public class InputTestsBase extends ServiceTestCase { mLatinIME.onCreateInputView(); mLatinIME.onStartInputView(ei, false); mInputConnection = ic; - mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); changeLanguage("en_US"); } @@ -253,6 +252,8 @@ public class InputTestsBase extends ServiceTestCase { fail("InputMethodSubtype for locale " + locale + " is not enabled"); } SubtypeSwitcher.getInstance().updateSubtype(subtype); + mLatinIME.loadKeyboard(); + mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); waitForDictionaryToBeLoaded(); } From dde36ef34329164cf8b8a3985c578dab0343b3eb Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 22 Aug 2012 14:15:56 +0900 Subject: [PATCH 105/133] Remove TextView reference from PointerTracker Change-Id: If8e65c08ec97f6d57932c9e3412d6fe17b0994ea --- .../inputmethod/keyboard/KeyboardView.java | 37 ++++++++++++++----- .../inputmethod/keyboard/PointerTracker.java | 12 +----- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index ff428ac85..0a70605d7 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -107,6 +107,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Key preview private final int mKeyPreviewLayoutId; + private final SparseArray mKeyPreviewTexts = CollectionUtils.newSparseArray(); protected final KeyPreviewDrawParams mKeyPreviewDrawParams; private boolean mShowKeyPreviewPopup = true; private int mDelayAfterPreview; @@ -152,7 +153,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_DISMISS_KEY_PREVIEW: - tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); + final TextView previewText = keyboardView.mKeyPreviewTexts.get(tracker.mPointerId); + if (previewText != null) { + previewText.setVisibility(INVISIBLE); + } break; } } @@ -165,7 +169,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker); } - public void cancelAllDismissKeyPreviews() { + private void cancelAllDismissKeyPreviews() { removeMessages(MSG_DISMISS_KEY_PREVIEW); } @@ -907,15 +911,30 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } } - // Called by {@link PointerTracker} constructor to create a TextView. - @Override - public TextView inflateKeyPreviewText() { + private TextView getKeyPreviewText(final int pointerId) { + TextView previewText = mKeyPreviewTexts.get(pointerId); + if (previewText != null) { + return previewText; + } final Context context = getContext(); if (mKeyPreviewLayoutId != 0) { - return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); + previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); } else { - return new TextView(context); + previewText = new TextView(context); } + mKeyPreviewTexts.put(pointerId, previewText); + return previewText; + } + + private void dismissAllKeyPreviews() { + final int pointerCount = mKeyPreviewTexts.size(); + for (int id = 0; id < pointerCount; id++) { + final TextView previewText = mKeyPreviewTexts.get(id); + if (previewText != null) { + previewText.setVisibility(INVISIBLE); + } + } + PointerTracker.setReleasedKeyGraphicsToAllKeys(); } @Override @@ -971,7 +990,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public void showKeyPreview(PointerTracker tracker) { if (!mShowKeyPreviewPopup) return; - final TextView previewText = tracker.getKeyPreviewText(); + final TextView previewText = getKeyPreviewText(tracker.mPointerId); // If the key preview has no parent view yet, add it to the ViewGroup which can place // key preview absolutely in SoftInputWindow. if (previewText.getParent() == null) { @@ -1082,7 +1101,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } public void closing() { - PointerTracker.dismissAllKeyPreviews(); + dismissAllKeyPreviews(); cancelAllMessages(); mInvalidateAllKeys = true; diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 9d953202e..d9a7cb418 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -21,8 +21,6 @@ import android.graphics.Paint; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; -import android.view.View; -import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; @@ -79,7 +77,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { public interface DrawingProxy extends MoreKeysPanel.Controller { public void invalidateKey(Key key); - public TextView inflateKeyPreviewText(); public void showKeyPreview(PointerTracker tracker); public void dismissKeyPreview(PointerTracker tracker); public void showGestureTrail(PointerTracker tracker); @@ -140,7 +137,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { private Keyboard mKeyboard; private int mKeyQuarterWidthSquared; - private final TextView mKeyPreviewText; private boolean mIsAlphabetKeyboard; private boolean mIsPossibleGesture = false; @@ -261,11 +257,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { updateGestureHandlingMode(); } - public static void dismissAllKeyPreviews() { + public static void setReleasedKeyGraphicsToAllKeys() { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); tracker.setReleasedKeyGraphics(tracker.mCurrentKey); } } @@ -312,11 +307,6 @@ public class PointerTracker implements PointerTrackerQueue.Element { mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); mTimerProxy = handler.getTimerProxy(); - mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText(); - } - - public TextView getKeyPreviewText() { - return mKeyPreviewText; } // Returns true if keyboard has been changed by this callback. From fa0bac9057e2dbb0b1aacc6d748a8bcf12ac1462 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 6 Aug 2012 18:29:51 -0700 Subject: [PATCH 106/133] ResearchLogging log when preferences change Bug: 6188932 Change-Id: I82a115aed30d7693f92d784a32b038996d8b71e3 --- .../inputmethod/research/ResearchLogger.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 45a33eaee..129a04dbf 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -417,6 +417,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang abort(); } requestIndicatorRedraw(); + mPrefs = prefs; + prefsChanged(prefs); } public void presentResearchDialog(final LatinIME latinIME) { @@ -741,6 +743,17 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "UserFeedback", "FeedbackContents" }; + private static final String[] EVENTKEYS_PREFS_CHANGED = { + "PrefsChanged", "prefs" + }; + public static void prefsChanged(final SharedPreferences prefs) { + final ResearchLogger researchLogger = getInstance(); + final Object[] values = { + prefs + }; + researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values); + } + // Regular logging methods private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = { From b5ace11a2e7b88e6b89b6297d5d540f351e48a4a Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Sat, 4 Aug 2012 23:26:35 -0700 Subject: [PATCH 107/133] ResearchLogging upload via service Upload using an intent service rather than just a thread. More robust in case the keyboard is closed and the upload hasn't finished yet. multi-project commit with Id515c2b1b0d6f85be50f92a661a2d32ebaeaa143 Bug: 6188932 Change-Id: I64a9fab92ff89ee09df877db4e5f7f0ec0ffb3b6 --- java/res/values/strings.xml | 4 + .../research/BootBroadcastReceiver.java | 33 +++ .../research/ResearchLogUploader.java | 240 ------------------ .../inputmethod/research/ResearchLogger.java | 46 +++- .../inputmethod/research/UploaderService.java | 191 ++++++++++++++ 5 files changed, 268 insertions(+), 246 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/BootBroadcastReceiver.java delete mode 100644 java/src/com/android/inputmethod/research/ResearchLogUploader.java create mode 100644 java/src/com/android/inputmethod/research/UploaderService.java diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index f2468f5a4..35cbcf3c4 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -289,6 +289,10 @@ Send usage info + + + Research Uploader Service + Input languages diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java new file mode 100644 index 000000000..5124a35a6 --- /dev/null +++ b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java @@ -0,0 +1,33 @@ +/* + * 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.research; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * Arrange for the uploading service to be run on regular intervals. + */ +public final class BootBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { + ResearchLogger.scheduleUploadingService(context); + } + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java deleted file mode 100644 index 9904a1de2..000000000 --- a/java/src/com/android/inputmethod/research/ResearchLogUploader.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.research; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.BatteryManager; -import android.util.Log; - -import com.android.inputmethod.latin.R; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -public final class ResearchLogUploader { - private static final String TAG = ResearchLogUploader.class.getSimpleName(); - private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min - private static final int BUF_SIZE = 1024 * 8; - protected static final int TIMEOUT_IN_MS = 1000 * 4; - - private final boolean mCanUpload; - private final Context mContext; - private final File mFilesDir; - private final URL mUrl; - private final ScheduledExecutorService mExecutor; - - public ResearchLogUploader(final Context context, final File filesDir) { - mContext = context; - mFilesDir = filesDir; - final PackageManager packageManager = context.getPackageManager(); - final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, - context.getPackageName()) == PackageManager.PERMISSION_GRANTED; - if (!hasPermission) { - mCanUpload = false; - mUrl = null; - mExecutor = null; - return; - } - URL tempUrl = null; - boolean canUpload = false; - ScheduledExecutorService executor = null; - try { - final String urlString = context.getString(R.string.research_logger_upload_url); - if (urlString == null || urlString.equals("")) { - return; - } - tempUrl = new URL(urlString); - canUpload = true; - executor = Executors.newSingleThreadScheduledExecutor(); - } catch (MalformedURLException e) { - tempUrl = null; - e.printStackTrace(); - return; - } finally { - mCanUpload = canUpload; - mUrl = tempUrl; - mExecutor = executor; - } - } - - public void start() { - if (mCanUpload) { - mExecutor.scheduleWithFixedDelay(new UploadRunnable(null /* logToWaitFor */, - null /* callback */, false /* forceUpload */), - UPLOAD_INTERVAL_IN_MS, UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } - } - - public void uploadAfterCompletion(final ResearchLog researchLog, final Callback callback) { - if (mCanUpload) { - mExecutor.submit(new UploadRunnable(researchLog, callback, true /* forceUpload */)); - } - } - - public void uploadNow(final Callback callback) { - // Perform an immediate upload. Note that this should happen even if there is - // another upload happening right now, as it may have missed the latest changes. - // TODO: Reschedule regular upload tests starting from now. - if (mCanUpload) { - mExecutor.submit(new UploadRunnable(null /* logToWaitFor */, callback, - true /* forceUpload */)); - } - } - - public interface Callback { - public void onUploadCompleted(final boolean success); - } - - private boolean isExternallyPowered() { - final Intent intent = mContext.registerReceiver(null, new IntentFilter( - Intent.ACTION_BATTERY_CHANGED)); - final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - return pluggedState == BatteryManager.BATTERY_PLUGGED_AC - || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; - } - - private boolean hasWifiConnection() { - final ConnectivityManager manager = - (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - return wifiInfo.isConnected(); - } - - class UploadRunnable implements Runnable { - private final ResearchLog mLogToWaitFor; - private final Callback mCallback; - private final boolean mForceUpload; - - public UploadRunnable(final ResearchLog logToWaitFor, final Callback callback, - final boolean forceUpload) { - mLogToWaitFor = logToWaitFor; - mCallback = callback; - mForceUpload = forceUpload; - } - - @Override - public void run() { - if (mLogToWaitFor != null) { - waitFor(mLogToWaitFor); - } - doUpload(); - } - - private void waitFor(final ResearchLog researchLog) { - try { - researchLog.awaitTermination(TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - private void doUpload() { - if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) { - return; - } - if (mFilesDir == null) { - return; - } - final File[] files = mFilesDir.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) - && !pathname.canWrite(); - } - }); - boolean success = true; - if (files.length == 0) { - success = false; - } - for (final File file : files) { - if (!uploadFile(file)) { - success = false; - } - } - if (mCallback != null) { - mCallback.onUploadCompleted(success); - } - } - - private boolean uploadFile(File file) { - Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); - boolean success = false; - final int contentLength = (int) file.length(); - HttpURLConnection connection = null; - InputStream fileIs = null; - try { - fileIs = new FileInputStream(file); - connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("PUT"); - connection.setDoOutput(true); - connection.setFixedLengthStreamingMode(contentLength); - final OutputStream os = connection.getOutputStream(); - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = fileIs.read(buf)) != -1) { - os.write(buf, 0, numBytesRead); - } - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.d(TAG, "upload failed: " + connection.getResponseCode()); - InputStream netIs = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(netIs)); - String line; - while ((line = reader.readLine()) != null) { - Log.d(TAG, "| " + reader.readLine()); - } - reader.close(); - return success; - } - file.delete(); - success = true; - Log.d(TAG, "upload successful"); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (fileIs != null) { - try { - fileIs.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (connection != null) { - connection.disconnect(); - } - } - return success; - } - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 129a04dbf..918fcf5a1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -18,11 +18,14 @@ package com.android.inputmethod.research; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; +import android.app.AlarmManager; import android.app.AlertDialog; import android.app.Dialog; +import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; +import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageInfo; @@ -139,7 +142,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private MainKeyboardView mMainKeyboardView; private InputMethodService mInputMethodService; private final Statistics mStatistics; - private ResearchLogUploader mResearchLogUploader; + + private Intent mUploadIntent; + private PendingIntent mUploadPendingIntent; private LogUnit mCurrentLogUnit = new LogUnit(); @@ -181,10 +186,33 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang e.apply(); } } - mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir); - mResearchLogUploader.start(); mInputMethodService = ims; mPrefs = prefs; + mUploadIntent = new Intent(mInputMethodService, UploaderService.class); + mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0); + + if (ProductionFlag.IS_EXPERIMENTAL) { + scheduleUploadingService(mInputMethodService); + } + } + + /** + * Arrange for the UploaderService to be run on a regular basis. + * + * Any existing scheduled invocation of UploaderService is removed and rescheduled. This may + * cause problems if this method is called often and frequent updates are required, but since + * the user will likely be sleeping at some point, if the interval is less that the expected + * sleep duration and this method is not called during that time, the service should be invoked + * at some point. + */ + public static void scheduleUploadingService(Context context) { + final Intent intent = new Intent(context, UploaderService.class); + final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); + final AlarmManager manager = + (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + manager.cancel(pendingIntent); + manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, + UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent); } private void cleanupLoggingDir(final File dir, final long time) { @@ -267,6 +295,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Editor e = mPrefs.edit(); e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true); e.apply(); + restart(); } private void setLoggingAllowed(boolean enableLogging) { @@ -489,10 +518,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mFeedbackLogBuffer == null) { return; } - if (!includeHistory) { + if (includeHistory) { + commitCurrentLogUnit(); + } else { mFeedbackLogBuffer.clear(); } - commitCurrentLogUnit(); final LogUnit feedbackLogUnit = new LogUnit(); final Object[] values = { feedbackContents @@ -502,10 +532,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mFeedbackLogBuffer.shiftIn(feedbackLogUnit); publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */); mFeedbackLog.close(); - mResearchLogUploader.uploadAfterCompletion(mFeedbackLog, null); + uploadNow(); mFeedbackLog = new ResearchLog(createLogFile(mFilesDir)); } + public void uploadNow() { + mInputMethodService.startService(mUploadIntent); + } + public void onLeavingSendFeedbackDialog() { mInFeedbackDialog = false; } diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java new file mode 100644 index 000000000..7a5749096 --- /dev/null +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -0,0 +1,191 @@ +/* + * 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.research; + +import android.Manifest; +import android.app.AlarmManager; +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.BatteryManager; +import android.os.Bundle; +import android.util.Log; + +import com.android.inputmethod.latin.R; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public final class UploaderService extends IntentService { + private static final String TAG = UploaderService.class.getSimpleName(); + public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; + private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() + + ".extra.UPLOAD_UNCONDITIONALLY"; + private static final int BUF_SIZE = 1024 * 8; + protected static final int TIMEOUT_IN_MS = 1000 * 4; + + private boolean mCanUpload; + private File mFilesDir; + private URL mUrl; + + public UploaderService() { + super("Research Uploader Service"); + } + + @Override + public void onCreate() { + super.onCreate(); + + mCanUpload = false; + mFilesDir = null; + mUrl = null; + + final PackageManager packageManager = getPackageManager(); + final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, + getPackageName()) == PackageManager.PERMISSION_GRANTED; + if (!hasPermission) { + return; + } + + try { + final String urlString = getString(R.string.research_logger_upload_url); + if (urlString == null || urlString.equals("")) { + return; + } + mFilesDir = getFilesDir(); + mUrl = new URL(urlString); + mCanUpload = true; + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + + @Override + protected void onHandleIntent(Intent intent) { + if (!mCanUpload) { + return; + } + boolean isUploadingUnconditionally = false; + Bundle bundle = intent.getExtras(); + if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { + isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); + } + doUpload(isUploadingUnconditionally); + } + + private boolean isExternallyPowered() { + final Intent intent = registerReceiver(null, new IntentFilter( + Intent.ACTION_BATTERY_CHANGED)); + final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return pluggedState == BatteryManager.BATTERY_PLUGGED_AC + || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; + } + + private boolean hasWifiConnection() { + final ConnectivityManager manager = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } + + private void doUpload(final boolean isUploadingUnconditionally) { + if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) { + return; + } + if (mFilesDir == null) { + return; + } + final File[] files = mFilesDir.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX) + && !pathname.canWrite(); + } + }); + boolean success = true; + if (files.length == 0) { + success = false; + } + for (final File file : files) { + if (!uploadFile(file)) { + success = false; + } + } + } + + private boolean uploadFile(File file) { + Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); + boolean success = false; + final int contentLength = (int) file.length(); + HttpURLConnection connection = null; + InputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(file); + connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(contentLength); + final OutputStream os = connection.getOutputStream(); + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = fileInputStream.read(buf)) != -1) { + os.write(buf, 0, numBytesRead); + } + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.d(TAG, "upload failed: " + connection.getResponseCode()); + InputStream netInputStream = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream)); + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "| " + reader.readLine()); + } + reader.close(); + return success; + } + file.delete(); + success = true; + Log.d(TAG, "upload successful"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (fileInputStream != null) { + try { + fileInputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (connection != null) { + connection.disconnect(); + } + } + return success; + } +} From 694ccb5ba1bb61caea379487c6bdfae7c64c2643 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Aug 2012 15:14:46 +0900 Subject: [PATCH 108/133] Cosmetic change to PointerTracker * Add final to parameters and locals * Add curly bracket around single statement if-block Change-Id: I9f3b23f7145e6b1d6ef806d991ed71ebc63311ff --- .../inputmethod/keyboard/PointerTracker.java | 122 ++++++++++-------- 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index d9a7cb418..ac1a28b05 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -196,7 +196,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT); } - public static void setParameters(MainKeyboardView.PointerTrackerParams params) { + public static void setParameters(final MainKeyboardView.PointerTrackerParams params) { sParams = params; sTouchNoiseThresholdDistanceSquared = (int)( params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); @@ -210,17 +210,17 @@ public class PointerTracker implements PointerTrackerQueue.Element { } // Note that this method is called from a non-UI thread. - public static void setMainDictionaryAvailability(boolean mainDictionaryAvailable) { + public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) { sMainDictionaryAvailable = mainDictionaryAvailable; updateGestureHandlingMode(); } - public static void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) { + public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) { sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser; updateGestureHandlingMode(); } - public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) { + public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) { final ArrayList trackers = sTrackers; // Create pointer trackers until we can get 'id+1'-th tracker, if needed. @@ -236,7 +236,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false; } - public static void setKeyboardActionListener(KeyboardActionListener listener) { + public static void setKeyboardActionListener(final KeyboardActionListener listener) { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); @@ -244,7 +244,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public static void setKeyDetector(KeyDetector keyDetector) { + public static void setKeyDetector(final KeyDetector keyDetector) { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); @@ -298,9 +298,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { sAggregratedPointers.reset(); } - private PointerTracker(int id, KeyEventHandler handler) { - if (handler == null) + private PointerTracker(final int id, final KeyEventHandler handler) { + if (handler == null) { throw new NullPointerException(); + } mPointerId = id; mGestureStroke = new GestureStroke(id); setKeyDetectorInner(handler.getKeyDetector()); @@ -310,7 +311,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } // Returns true if keyboard has been changed by this callback. - private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) { + private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) { if (mInGesture) { return false; } @@ -335,7 +336,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { // Note that we need primaryCode argument because the keyboard may in shifted state and the // primaryCode is different from {@link Key#mCode}. - private void callListenerOnCodeInput(Key key, int primaryCode, int x, int y) { + private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x, + final int y) { final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); final int code = altersCode ? key.mAltCode : primaryCode; @@ -364,7 +366,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { // Note that we need primaryCode argument because the keyboard may in shifted state and the // primaryCode is different from {@link Key#mCode}. - private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { + private void callListenerOnRelease(final Key key, final int primaryCode, + final boolean withSliding) { if (mInGesture) { return; } @@ -387,15 +390,16 @@ public class PointerTracker implements PointerTrackerQueue.Element { } private void callListenerOnCancelInput() { - if (DEBUG_LISTENER) + if (DEBUG_LISTENER) { Log.d(TAG, "onCancelInput"); + } if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.pointerTracker_callListenerOnCancelInput(); } mListener.onCancelInput(); } - private void setKeyDetectorInner(KeyDetector keyDetector) { + private void setKeyDetectorInner(final KeyDetector keyDetector) { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard(); @@ -425,11 +429,11 @@ public class PointerTracker implements PointerTrackerQueue.Element { return mCurrentKey != null && mCurrentKey.isModifier(); } - public Key getKeyOn(int x, int y) { + public Key getKeyOn(final int x, final int y) { return mKeyDetector.detectHitKey(x, y); } - private void setReleasedKeyGraphics(Key key) { + private void setReleasedKeyGraphics(final Key key) { mDrawingProxy.dismissKeyPreview(this); if (key == null) { return; @@ -460,7 +464,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void setPressedKeyGraphics(Key key) { + private void setPressedKeyGraphics(final Key key) { if (key == null) { return; } @@ -499,17 +503,17 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void updateReleaseKeyGraphics(Key key) { + private void updateReleaseKeyGraphics(final Key key) { key.onReleased(); mDrawingProxy.invalidateKey(key); } - private void updatePressKeyGraphics(Key key) { + private void updatePressKeyGraphics(final Key key) { key.onPressed(); mDrawingProxy.invalidateKey(key); } - public void drawGestureTrail(Canvas canvas, Paint paint) { + public void drawGestureTrail(final Canvas canvas, final Paint paint) { if (mInGesture) { mGestureStroke.drawGestureTrail(canvas, paint); } @@ -527,22 +531,22 @@ public class PointerTracker implements PointerTrackerQueue.Element { return mDownTime; } - private Key onDownKey(int x, int y, long eventTime) { + private Key onDownKey(final int x, final int y, final long eventTime) { mDownTime = eventTime; return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); } - private Key onMoveKeyInternal(int x, int y) { + private Key onMoveKeyInternal(final int x, final int y) { mLastX = x; mLastY = y; return mKeyDetector.detectHitKey(x, y); } - private Key onMoveKey(int x, int y) { + private Key onMoveKey(final int x, final int y) { return onMoveKeyInternal(x, y); } - private Key onMoveToNewKey(Key newKey, int x, int y) { + private Key onMoveToNewKey(final Key newKey, final int x, final int y) { mCurrentKey = newKey; mKeyX = x; mKeyY = y; @@ -557,14 +561,14 @@ public class PointerTracker implements PointerTrackerQueue.Element { mListener.onStartBatchInput(); } - private void updateBatchInput(InputPointers batchPoints) { + private void updateBatchInput(final InputPointers batchPoints) { if (DEBUG_LISTENER) { Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize()); } mListener.onUpdateBatchInput(batchPoints); } - private void endBatchInput(InputPointers batchPoints) { + private void endBatchInput(final InputPointers batchPoints) { if (DEBUG_LISTENER) { Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize()); } @@ -585,7 +589,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { mLastRecognitionTime = 0; } - private boolean updateBatchInputRecognitionState(long eventTime, int size) { + private boolean updateBatchInputRecognitionState(final long eventTime, final int size) { if (size > mLastRecognitionPointSize && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) { mLastRecognitionPointSize = size; @@ -595,8 +599,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { return false; } - public void processMotionEvent(int action, int x, int y, long eventTime, - KeyEventHandler handler) { + public void processMotionEvent(final int action, final int x, final int y, final long eventTime, + final KeyEventHandler handler) { switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: @@ -615,9 +619,11 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) { - if (DEBUG_EVENT) + public void onDownEvent(final int x, final int y, final long eventTime, + final KeyEventHandler handler) { + if (DEBUG_EVENT) { printTouchEvent("onDownEvent:", x, y, eventTime); + } mDrawingProxy = handler.getDrawingProxy(); mTimerProxy = handler.getTimerProxy(); @@ -665,7 +671,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void onDownEventInternal(int x, int y, long eventTime) { + private void onDownEventInternal(final int x, final int y, final long eventTime) { Key key = onDownKey(x, y, eventTime); // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding // from modifier key, or 3) this pointer's KeyDetector always allows sliding input. @@ -690,15 +696,15 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void startSlidingKeyInput(Key key) { + private void startSlidingKeyInput(final Key key) { if (!mIsInSlidingKeyInput) { mIgnoreModifierKey = key.isModifier(); } mIsInSlidingKeyInput = true; } - private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime, - boolean isHistorical, Key key) { + private void onGestureMoveEvent(final PointerTracker tracker, final int x, final int y, + final long eventTime, final boolean isHistorical, final Key key) { final int gestureTime = (int)(eventTime - tracker.getDownTime()); if (sShouldHandleGesture && mIsPossibleGesture) { final GestureStroke stroke = mGestureStroke; @@ -717,11 +723,13 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) { - if (DEBUG_MOVE_EVENT) + public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) { + if (DEBUG_MOVE_EVENT) { printTouchEvent("onMoveEvent:", x, y, eventTime); - if (mKeyAlreadyProcessed) + } + if (mKeyAlreadyProcessed) { return; + } if (me != null) { // Add historical points to gesture path. @@ -841,9 +849,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public void onUpEvent(int x, int y, long eventTime) { - if (DEBUG_EVENT) + public void onUpEvent(final int x, final int y, final long eventTime) { + if (DEBUG_EVENT) { printTouchEvent("onUpEvent :", x, y, eventTime); + } final PointerTrackerQueue queue = sPointerTrackerQueue; if (queue != null) { @@ -865,9 +874,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a // "virtual" up event. @Override - public void onPhantomUpEvent(long eventTime) { - if (DEBUG_EVENT) + public void onPhantomUpEvent(final long eventTime) { + if (DEBUG_EVENT) { printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime); + } onUpEventInternal(); mKeyAlreadyProcessed = true; } @@ -898,14 +908,15 @@ public class PointerTracker implements PointerTrackerQueue.Element { // This event will be recognized as a regular code input. Clear unused batch points so they // are not mistakenly included in the next batch event. clearBatchInputPointsOfAllPointerTrackers(); - if (mKeyAlreadyProcessed) + if (mKeyAlreadyProcessed) { return; + } if (mCurrentKey != null && !mCurrentKey.isRepeatable()) { detectAndSendKey(mCurrentKey, mKeyX, mKeyY); } } - public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) { + public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) { abortBatchInput(); onLongPressed(); mIsShowingMoreKeysPanel = true; @@ -921,9 +932,10 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - public void onCancelEvent(int x, int y, long eventTime) { - if (DEBUG_EVENT) + public void onCancelEvent(final int x, final int y, final long eventTime) { + if (DEBUG_EVENT) { printTouchEvent("onCancelEvt:", x, y, eventTime); + } final PointerTrackerQueue queue = sPointerTrackerQueue; if (queue != null) { @@ -943,24 +955,25 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void startRepeatKey(Key key) { + private void startRepeatKey(final Key key) { if (key != null && key.isRepeatable() && !mInGesture) { onRegisterKey(key); mTimerProxy.startKeyRepeatTimer(this); } } - public void onRegisterKey(Key key) { + public void onRegisterKey(final Key key) { if (key != null) { detectAndSendKey(key, key.mX, key.mY); mTimerProxy.startTypingStateTimer(key); } } - private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) { - if (mKeyDetector == null) + private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final Key newKey) { + if (mKeyDetector == null) { throw new NullPointerException("keyboard and/or key detector not set"); - Key curKey = mCurrentKey; + } + final Key curKey = mCurrentKey; if (newKey == curKey) { return false; } else if (curKey != null) { @@ -971,24 +984,25 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } - private void startLongPressTimer(Key key) { + private void startLongPressTimer(final Key key) { if (key != null && key.isLongPressEnabled() && !mInGesture) { mTimerProxy.startLongPressTimer(this); } } - private void detectAndSendKey(Key key, int x, int y) { + private void detectAndSendKey(final Key key, final int x, final int y) { if (key == null) { callListenerOnCancelInput(); return; } - int code = key.mCode; + final int code = key.mCode; callListenerOnCodeInput(key, code, x, y); callListenerOnRelease(key, code, false); } - private void printTouchEvent(String title, int x, int y, long eventTime) { + private void printTouchEvent(final String title, final int x, final int y, + final long eventTime) { final Key key = mKeyDetector.detectHitKey(x, y); final String code = KeyDetector.printableCode(key); Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title, From b28139f51b2f2e6dfd60e968024006f53590a41c Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Aug 2012 15:54:00 +0900 Subject: [PATCH 109/133] Use PointerTrackerQueue.size instead of MotionEvent.getPointerCount Change-Id: I089e905def0b70bd38fd54ab2d84689b904bb320 --- .../src/com/android/inputmethod/keyboard/PointerTracker.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index ac1a28b05..751dd6061 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -819,8 +819,9 @@ public class PointerTracker implements PointerTrackerQueue.Element { // touch panels when there are close multiple touches. // Caveat: When in chording input mode with a modifier key, we don't use // this hack. - if (me != null && me.getPointerCount() > 1 - && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) { + final PointerTrackerQueue queue = sPointerTrackerQueue; + if (queue != null && queue.size() > 1 + && !queue.hasModifierKeyOlderThan(this)) { onUpEventInternal(); } if (!mIsPossibleGesture) { From 5509798977a61dcb4a9dde9030f31bb138b71e3b Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Aug 2012 14:28:15 +0900 Subject: [PATCH 110/133] Remeve the reference of MainKeyboardView from PointerTracker This change also consolidates MainKeyboardView.KeyTimerParams with KeyTimerHandler. Change-Id: Ifc70ca81fa7d7daded4c2204fcdffdf942c16bc5 --- .../keyboard/MainKeyboardView.java | 88 ++++++------------- .../inputmethod/keyboard/PointerTracker.java | 44 ++++++++-- 2 files changed, 62 insertions(+), 70 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 1b309a439..0cc0b6320 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -110,7 +110,6 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key new WeakHashMap(); private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; - private final PointerTrackerParams mPointerTrackerParams; private final SuddenJumpingTouchEventHandler mTouchScreenRegulator; protected KeyDetector mKeyDetector; @@ -127,11 +126,26 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key private static final int MSG_LONGPRESS_KEY = 2; private static final int MSG_DOUBLE_TAP = 3; - private final KeyTimerParams mParams; + private final int mKeyRepeatStartTimeout; + private final int mKeyRepeatInterval; + private final int mLongPressKeyTimeout; + private final int mLongPressShiftKeyTimeout; + private final int mIgnoreAltCodeKeyTimeout; - public KeyTimerHandler(MainKeyboardView outerInstance, KeyTimerParams params) { + public KeyTimerHandler(final MainKeyboardView outerInstance, + final TypedArray mainKeyboardViewAttr) { super(outerInstance); - mParams = params; + + mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0); + mKeyRepeatInterval = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyRepeatInterval, 0); + mLongPressKeyTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_longPressKeyTimeout, 0); + mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0); + mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); } @Override @@ -146,7 +160,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key final Key currentKey = tracker.getKey(); if (currentKey != null && currentKey.mCode == msg.arg1) { tracker.onRegisterKey(currentKey); - startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval); + startKeyRepeatTimer(tracker, mKeyRepeatInterval); } break; case MSG_LONGPRESS_KEY: @@ -167,7 +181,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key @Override public void startKeyRepeatTimer(PointerTracker tracker) { - startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout); + startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout); } public void cancelKeyRepeatTimer() { @@ -185,7 +199,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key final int delay; switch (code) { case Keyboard.CODE_SHIFT: - delay = mParams.mLongPressShiftKeyTimeout; + delay = mLongPressShiftKeyTimeout; break; default: delay = 0; @@ -206,15 +220,15 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key final int delay; switch (key.mCode) { case Keyboard.CODE_SHIFT: - delay = mParams.mLongPressShiftKeyTimeout; + delay = mLongPressShiftKeyTimeout; break; default: if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) { // We use longer timeout for sliding finger input started from the symbols // mode key. - delay = mParams.mLongPressKeyTimeout * 3; + delay = mLongPressKeyTimeout * 3; } else { - delay = mParams.mLongPressKeyTimeout; + delay = mLongPressKeyTimeout; } break; } @@ -268,7 +282,7 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key } sendMessageDelayed( - obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout); + obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout); if (isTyping) { return; } @@ -307,50 +321,6 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key } } - public static class PointerTrackerParams { - public final boolean mSlidingKeyInputEnabled; - public final int mTouchNoiseThresholdTime; - public final float mTouchNoiseThresholdDistance; - - public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); - - private PointerTrackerParams() { - mSlidingKeyInputEnabled = false; - mTouchNoiseThresholdTime =0; - mTouchNoiseThresholdDistance = 0; - } - - public PointerTrackerParams(TypedArray mainKeyboardViewAttr) { - mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean( - R.styleable.MainKeyboardView_slidingKeyInputEnable, false); - mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0); - mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimension( - R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0); - } - } - - static class KeyTimerParams { - public final int mKeyRepeatStartTimeout; - public final int mKeyRepeatInterval; - public final int mLongPressKeyTimeout; - public final int mLongPressShiftKeyTimeout; - public final int mIgnoreAltCodeKeyTimeout; - - public KeyTimerParams(TypedArray mainKeyboardViewAttr) { - mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0); - mKeyRepeatInterval = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_keyRepeatInterval, 0); - mLongPressKeyTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_longPressKeyTimeout, 0); - mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0); - mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); - } - } - public MainKeyboardView(Context context, AttributeSet attrs) { this(context, attrs, R.attr.mainKeyboardViewStyle); } @@ -389,19 +359,15 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId( R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0); - final KeyTimerParams keyTimerParams = new KeyTimerParams(a); - mPointerTrackerParams = new PointerTrackerParams(a); - final float keyHysteresisDistance = a.getDimension( R.styleable.MainKeyboardView_keyHysteresisDistance, 0); mKeyDetector = new KeyDetector(keyHysteresisDistance); - mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams); + mKeyTimerHandler = new KeyTimerHandler(this, a); mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean( R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); + PointerTracker.setParameters(a); a.recycle(); - PointerTracker.setParameters(mPointerTrackerParams); - mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator( languageOnSpacebarFadeoutAnimatorResId, this); mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator( diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index ac1a28b05..fd4177b72 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.os.SystemClock; @@ -28,6 +29,7 @@ import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.research.ResearchLogger; @@ -118,9 +120,36 @@ public class PointerTracker implements PointerTrackerQueue.Element { } } + static class PointerTrackerParams { + public final boolean mSlidingKeyInputEnabled; + public final int mTouchNoiseThresholdTime; + public final float mTouchNoiseThresholdDistance; + public final int mTouchNoiseThresholdDistanceSquared; + + public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); + + private PointerTrackerParams() { + mSlidingKeyInputEnabled = false; + mTouchNoiseThresholdTime = 0; + mTouchNoiseThresholdDistance = 0.0f; + mTouchNoiseThresholdDistanceSquared = 0; + } + + public PointerTrackerParams(TypedArray mainKeyboardViewAttr) { + mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean( + R.styleable.MainKeyboardView_slidingKeyInputEnable, false); + mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0); + final float touchNouseThresholdDistance = mainKeyboardViewAttr.getDimension( + R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0); + mTouchNoiseThresholdDistance = touchNouseThresholdDistance; + mTouchNoiseThresholdDistanceSquared = + (int)(touchNouseThresholdDistance * touchNouseThresholdDistance); + } + } + // Parameters for pointer handling. - private static MainKeyboardView.PointerTrackerParams sParams; - private static int sTouchNoiseThresholdDistanceSquared; + private static PointerTrackerParams sParams; private static boolean sNeedsPhantomSuddenMoveEventHack; private static final ArrayList sTrackers = CollectionUtils.newArrayList(); @@ -192,14 +221,11 @@ public class PointerTracker implements PointerTrackerQueue.Element { sPointerTrackerQueue = null; } sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack; - - setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT); + sParams = PointerTrackerParams.DEFAULT; } - public static void setParameters(final MainKeyboardView.PointerTrackerParams params) { - sParams = params; - sTouchNoiseThresholdDistanceSquared = (int)( - params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance); + public static void setParameters(final TypedArray mainKeyboardViewAttr) { + sParams = new PointerTrackerParams(mainKeyboardViewAttr); } private static void updateGestureHandlingMode() { @@ -635,7 +661,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int dx = x - mLastX; final int dy = y - mLastY; final int distanceSquared = (dx * dx + dy * dy); - if (distanceSquared < sTouchNoiseThresholdDistanceSquared) { + if (distanceSquared < sParams.mTouchNoiseThresholdDistanceSquared) { if (DEBUG_MODE) Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT + " distance=" + distanceSquared); From 64ee09610024eb1436c51f9c9ef9fc3f77239d73 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Aug 2012 17:29:36 +0900 Subject: [PATCH 111/133] Add toString method to InputPointers and ResizableIntArray Change-Id: I835ee131855bef8233cf2672e2fb288b939148a6 --- .../com/android/inputmethod/latin/InputPointers.java | 6 ++++++ .../android/inputmethod/latin/ResizableIntArray.java | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index cbc916a7e..c01a373f2 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -124,4 +124,10 @@ public class InputPointers { public int[] getTimes() { return mTimes.getPrimitiveArray(); } + + @Override + public String toString() { + return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes + + " x=" + mXCoordinates + " y=" + mYCoordinates; + } } diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java index 387d45a53..c660f92c4 100644 --- a/java/src/com/android/inputmethod/latin/ResizableIntArray.java +++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java @@ -131,4 +131,16 @@ public class ResizableIntArray { mLength = endPos; } } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < mLength; i++) { + if (i != 0) { + sb.append(","); + } + sb.append(mArray[i]); + } + return "[" + sb + "]"; + } } From 64a26b4389abb273afff7699a8c86596defd85bf Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Aug 2012 17:47:13 +0900 Subject: [PATCH 112/133] Fix bug in InputPointers.append Change-Id: I39825757bbae32b66c4b61cb6c4fbcaa2e0c9c47 --- .../inputmethod/latin/InputPointers.java | 2 +- .../inputmethod/latin/InputPointersTests.java | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java index c01a373f2..ff2feb51d 100644 --- a/java/src/com/android/inputmethod/latin/InputPointers.java +++ b/java/src/com/android/inputmethod/latin/InputPointers.java @@ -93,7 +93,7 @@ public class InputPointers { } mXCoordinates.append(xCoordinates, startPos, length); mYCoordinates.append(yCoordinates, startPos, length); - mPointerIds.fill(pointerId, startPos, length); + mPointerIds.fill(pointerId, mPointerIds.getLength(), length); mTimes.append(times, startPos, length); } diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java index 6f04f3ebb..cc55076c0 100644 --- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java +++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java @@ -18,6 +18,8 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; +import java.util.Arrays; + public class InputPointersTests extends AndroidTestCase { private static final int DEFAULT_CAPACITY = 48; @@ -162,6 +164,61 @@ public class InputPointersTests extends AndroidTestCase { src.getTimes(), 0, dst.getTimes(), dstLen, srcLen); } + public void testAppendResizableIntArray() { + final int srcLen = 100; + final int srcPointerId = 1; + final int[] srcPointerIds = new int[srcLen]; + Arrays.fill(srcPointerIds, srcPointerId); + final ResizableIntArray srcTimes = new ResizableIntArray(DEFAULT_CAPACITY); + final ResizableIntArray srcXCoords = new ResizableIntArray(DEFAULT_CAPACITY); + final ResizableIntArray srcYCoords= new ResizableIntArray(DEFAULT_CAPACITY); + for (int i = 0; i < srcLen; i++) { + srcTimes.add(i * 2); + srcXCoords.add(i * 3); + srcYCoords.add(i * 4); + } + final int dstLen = 50; + final InputPointers dst = new InputPointers(DEFAULT_CAPACITY); + for (int i = 0; i < dstLen; i++) { + final int value = -i - 1; + dst.addPointer(value * 4, value * 3, value * 2, value); + } + final InputPointers dstCopy = new InputPointers(DEFAULT_CAPACITY); + dstCopy.copy(dst); + + dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, 0); + assertEquals("size after append zero", dstLen, dst.getPointerSize()); + assertArrayEquals("xCoordinates after append zero", + dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen); + assertArrayEquals("yCoordinates after append zero", + dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen); + assertArrayEquals("pointerIds after append zero", + dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen); + assertArrayEquals("times after append zero", + dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen); + + dst.append(srcPointerId, srcTimes, srcXCoords, srcYCoords, 0, srcLen); + assertEquals("size after append", dstLen + srcLen, dst.getPointerSize()); + assertTrue("primitive length after append", + dst.getPointerIds().length >= dstLen + srcLen); + assertArrayEquals("original xCoordinates values after append", + dstCopy.getXCoordinates(), 0, dst.getXCoordinates(), 0, dstLen); + assertArrayEquals("original yCoordinates values after append", + dstCopy.getYCoordinates(), 0, dst.getYCoordinates(), 0, dstLen); + assertArrayEquals("original pointerIds values after append", + dstCopy.getPointerIds(), 0, dst.getPointerIds(), 0, dstLen); + assertArrayEquals("original times values after append", + dstCopy.getTimes(), 0, dst.getTimes(), 0, dstLen); + assertArrayEquals("appended xCoordinates values after append", + srcXCoords.getPrimitiveArray(), 0, dst.getXCoordinates(), dstLen, srcLen); + assertArrayEquals("appended yCoordinates values after append", + srcYCoords.getPrimitiveArray(), 0, dst.getYCoordinates(), dstLen, srcLen); + assertArrayEquals("appended pointerIds values after append", + srcPointerIds, 0, dst.getPointerIds(), dstLen, srcLen); + assertArrayEquals("appended times values after append", + srcTimes.getPrimitiveArray(), 0, dst.getTimes(), dstLen, srcLen); + } + private static void assertArrayEquals(String message, int[] expecteds, int expectedPos, int[] actuals, int actualPos, int length) { if (expecteds == null && actuals == null) { From 687a244703a02323ebd64433cbaead5def499861 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Thu, 23 Aug 2012 15:46:43 +0900 Subject: [PATCH 113/133] Step 38-A Cleanup touch path Change-Id: I9bfac98b9ac10f3812290f21673c4002241eae01 --- native/jni/src/correction.cpp | 122 +++++++++++----------- native/jni/src/correction.h | 16 +-- native/jni/src/defines.h | 3 + native/jni/src/geometry_utils.h | 1 - native/jni/src/proximity_info_state.cpp | 129 ++++++++++++++++++++---- native/jni/src/proximity_info_state.h | 39 +++++-- native/jni/src/unigram_dictionary.cpp | 68 ++++++------- native/jni/src/unigram_dictionary.h | 14 +-- 8 files changed, 252 insertions(+), 140 deletions(-) diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index 71f8a4fc8..342dec9a3 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -61,19 +61,19 @@ inline static void dumpEditDistance10ForDebug(int *editDistanceTable, } inline static void calcEditDistanceOneStep(int *editDistanceTable, const unsigned short *input, - const int inputLength, const unsigned short *output, const int outputLength) { + const int inputSize, const unsigned short *output, const int outputLength) { // TODO: Make sure that editDistance[0 ~ MAX_WORD_LENGTH_INTERNAL] is not touched. - // Let dp[i][j] be editDistanceTable[i * (inputLength + 1) + j]. - // Assuming that dp[0][0] ... dp[outputLength - 1][inputLength] are already calculated, - // and calculate dp[ouputLength][0] ... dp[outputLength][inputLength]. - int *const current = editDistanceTable + outputLength * (inputLength + 1); - const int *const prev = editDistanceTable + (outputLength - 1) * (inputLength + 1); + // Let dp[i][j] be editDistanceTable[i * (inputSize + 1) + j]. + // Assuming that dp[0][0] ... dp[outputLength - 1][inputSize] are already calculated, + // and calculate dp[ouputLength][0] ... dp[outputLength][inputSize]. + int *const current = editDistanceTable + outputLength * (inputSize + 1); + const int *const prev = editDistanceTable + (outputLength - 1) * (inputSize + 1); const int *const prevprev = - outputLength >= 2 ? editDistanceTable + (outputLength - 2) * (inputLength + 1) : 0; + outputLength >= 2 ? editDistanceTable + (outputLength - 2) * (inputSize + 1) : 0; current[0] = outputLength; const uint32_t co = toBaseLowerCase(output[outputLength - 1]); const uint32_t prevCO = outputLength >= 2 ? toBaseLowerCase(output[outputLength - 2]) : 0; - for (int i = 1; i <= inputLength; ++i) { + for (int i = 1; i <= inputSize; ++i) { const uint32_t ci = toBaseLowerCase(input[i - 1]); const uint16_t cost = (ci == co) ? 0 : 1; current[i] = min(current[i - 1] + 1, min(prev[i] + 1, prev[i - 1] + cost)); @@ -84,11 +84,11 @@ inline static void calcEditDistanceOneStep(int *editDistanceTable, const unsigne } inline static int getCurrentEditDistance(int *editDistanceTable, const int editDistanceTableWidth, - const int outputLength, const int inputLength) { + const int outputLength, const int inputSize) { if (DEBUG_EDIT_DISTANCE) { - AKLOGI("getCurrentEditDistance %d, %d", inputLength, outputLength); + AKLOGI("getCurrentEditDistance %d, %d", inputSize, outputLength); } - return editDistanceTable[(editDistanceTableWidth + 1) * (outputLength) + inputLength]; + return editDistanceTable[(editDistanceTableWidth + 1) * (outputLength) + inputSize]; } ////////////////////// @@ -109,12 +109,12 @@ void Correction::resetCorrection() { mTotalTraverseCount = 0; } -void Correction::initCorrection(const ProximityInfo *pi, const int inputLength, +void Correction::initCorrection(const ProximityInfo *pi, const int inputSize, const int maxDepth) { mProximityInfo = pi; - mInputLength = inputLength; + mInputSize = inputSize; mMaxDepth = maxDepth; - mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2; + mMaxEditDistance = mInputSize < 5 ? 2 : mInputSize / 2; // TODO: This is not supposed to be required. Check what's going wrong with // editDistance[0 ~ MAX_WORD_LENGTH_INTERNAL] initEditDistance(mEditDistanceTable); @@ -168,22 +168,22 @@ int Correction::getFreqForSplitMultipleWords(const int *freqArray, const int *wo } int Correction::getFinalProbability(const int probability, unsigned short **word, int *wordLength) { - return getFinalProbabilityInternal(probability, word, wordLength, mInputLength); + return getFinalProbabilityInternal(probability, word, wordLength, mInputSize); } int Correction::getFinalProbabilityForSubQueue(const int probability, unsigned short **word, - int *wordLength, const int inputLength) { - return getFinalProbabilityInternal(probability, word, wordLength, inputLength); + int *wordLength, const int inputSize) { + return getFinalProbabilityInternal(probability, word, wordLength, inputSize); } int Correction::getFinalProbabilityInternal(const int probability, unsigned short **word, - int *wordLength, const int inputLength) { + int *wordLength, const int inputSize) { const int outputIndex = mTerminalOutputIndex; const int inputIndex = mTerminalInputIndex; *wordLength = outputIndex + 1; *word = mWord; int finalProbability= Correction::RankingAlgorithm::calculateFinalProbability( - inputIndex, outputIndex, probability, mEditDistanceTable, this, inputLength); + inputIndex, outputIndex, probability, mEditDistanceTable, this, inputSize); return finalProbability; } @@ -270,13 +270,13 @@ bool Correction::needsToPrune() const { // TODO: use edit distance here return mOutputIndex - 1 >= mMaxDepth || mProximityCount > mMaxEditDistance // Allow one char longer word for missing character - || (!mDoAutoCompletion && (mOutputIndex > mInputLength)); + || (!mDoAutoCompletion && (mOutputIndex > mInputSize)); } void Correction::addCharToCurrentWord(const int32_t c) { mWord[mOutputIndex] = c; const unsigned short *primaryInputWord = mProximityInfoState.getPrimaryInputWord(); - calcEditDistanceOneStep(mEditDistanceTable, primaryInputWord, mInputLength, + calcEditDistanceOneStep(mEditDistanceTable, primaryInputWord, mInputSize, mWord, mOutputIndex + 1); } @@ -325,7 +325,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( // Skip checking this node if (mNeedsToTraverseAllNodes || isSingleQuote(c)) { bool incremented = false; - if (mLastCharExceeded && mInputIndex == mInputLength - 1) { + if (mLastCharExceeded && mInputIndex == mInputSize - 1) { // TODO: Do not check the proximity if EditDistance exceeds the threshold const ProximityType matchId = mProximityInfoState.getMatchedProximityId( mInputIndex, c, true, &proximityIndex); @@ -354,7 +354,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( if (mExcessiveCount == 0 && mExcessivePos < mOutputIndex) { mExcessivePos = mOutputIndex; } - if (mExcessivePos < mInputLength - 1) { + if (mExcessivePos < mInputSize - 1) { mExceeding = mExcessivePos == mInputIndex && canTryCorrection; } } @@ -373,7 +373,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( if (mTransposedCount == 0 && mTransposedPos < mOutputIndex) { mTransposedPos = mOutputIndex; } - if (mTransposedPos < mInputLength - 1) { + if (mTransposedPos < mInputSize - 1) { mTransposing = mInputIndex == mTransposedPos && canTryCorrection; } } @@ -392,7 +392,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( } else { --mTransposedCount; if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { DUMP_WORD(mWord, mOutputIndex); @@ -423,7 +423,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( && isEquivalentChar(mProximityInfoState.getMatchedProximityId( mInputIndex, mWord[mOutputIndex - 1], false))) { if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { AKLOGI("CONVERSION p->e %c", mWord[mOutputIndex - 1]); @@ -453,7 +453,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( // As the current char turned out to be an unrelated char, // we will try other correction-types. Please note that mCorrectionStates[mOutputIndex] // here refers to the previous state. - if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0 + if (mInputIndex < mInputSize - 1 && mOutputIndex > 0 && mTransposedCount > 0 && !mCorrectionStates[mOutputIndex].mTransposing && mCorrectionStates[mOutputIndex - 1].mTransposing && isEquivalentChar(mProximityInfoState.getMatchedProximityId( @@ -490,7 +490,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( ++mSkippedCount; --mProximityCount; return processSkipChar(c, isTerminal, false); - } else if (mInputIndex - 1 < mInputLength + } else if (mInputIndex - 1 < mInputSize && mSkippedCount > 0 && mCorrectionStates[mOutputIndex].mSkipping && mCorrectionStates[mOutputIndex].mAdditionalProximityMatching @@ -502,7 +502,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( mProximityMatching = true; ++mProximityCount; mDistances[mOutputIndex] = ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO; - } else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength + } else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputSize && isEquivalentChar( mProximityInfoState.getMatchedProximityId(mInputIndex + 1, c, false))) { // 1.2. Excessive or transpose correction @@ -513,7 +513,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( incrementInputIndex(); } if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { DUMP_WORD(mWord, mOutputIndex); @@ -529,7 +529,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( // 3. Skip correction ++mSkippedCount; if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { AKLOGI("SKIP: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount, @@ -542,7 +542,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( ++mProximityCount; mDistances[mOutputIndex] = ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO; if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { AKLOGI("ADDITIONALPROX: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount, @@ -550,7 +550,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( } } else { if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { DUMP_WORD(mWord, mOutputIndex); @@ -560,7 +560,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( return processUnrelatedCorrectionType(); } } else if (secondTransposing) { - // If inputIndex is greater than mInputLength, that means there is no + // If inputIndex is greater than mInputSize, that means there is no // proximity chars. So, we don't need to check proximity. mMatching = true; } else if (isEquivalentChar(matchedProximityCharId)) { @@ -573,7 +573,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( mDistances[mOutputIndex] = mProximityInfoState.getNormalizedSquaredDistance(mInputIndex, proximityIndex); if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { AKLOGI("PROX: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount, @@ -585,8 +585,8 @@ Correction::CorrectionType Correction::processCharAndCalcState( // 4. Last char excessive correction mLastCharExceeded = mExcessiveCount == 0 && mSkippedCount == 0 && mTransposedCount == 0 - && mProximityCount == 0 && (mInputIndex == mInputLength - 2); - const bool isSameAsUserTypedLength = (mInputLength == mInputIndex + 1) || mLastCharExceeded; + && mProximityCount == 0 && (mInputIndex == mInputSize - 2); + const bool isSameAsUserTypedLength = (mInputSize == mInputIndex + 1) || mLastCharExceeded; if (mLastCharExceeded) { ++mExcessiveCount; } @@ -597,7 +597,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( } const bool needsToTryOnTerminalForTheLastPossibleExcessiveChar = - mExceeding && mInputIndex == mInputLength - 2; + mExceeding && mInputIndex == mInputSize - 2; // Finally, we are ready to go to the next character, the next "virtual node". // We should advance the input index. @@ -613,7 +613,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( mTerminalInputIndex = mInputIndex - 1; mTerminalOutputIndex = mOutputIndex - 1; if (DEBUG_CORRECTION - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength) + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputSize) && (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) { DUMP_WORD(mWord, mOutputIndex); AKLOGI("ONTERMINAL(1): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount, @@ -651,7 +651,7 @@ inline static bool isUpperCase(unsigned short c) { /* static */ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex, const int outputIndex, const int freq, int *editDistanceTable, const Correction *correction, - const int inputLength) { + const int inputSize) { const int excessivePos = correction->getExcessivePos(); const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER; const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER; @@ -663,55 +663,55 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex const bool lastCharExceeded = correction->mLastCharExceeded; const bool useFullEditDistance = correction->mUseFullEditDistance; const int outputLength = outputIndex + 1; - if (skippedCount >= inputLength || inputLength == 0) { + if (skippedCount >= inputSize || inputSize == 0) { return -1; } // TODO: find more robust way - bool sameLength = lastCharExceeded ? (inputLength == inputIndex + 2) - : (inputLength == inputIndex + 1); + bool sameLength = lastCharExceeded ? (inputSize == inputIndex + 2) + : (inputSize == inputIndex + 1); // TODO: use mExcessiveCount - const int matchCount = inputLength - correction->mProximityCount - excessiveCount; + const int matchCount = inputSize - correction->mProximityCount - excessiveCount; const unsigned short *word = correction->mWord; const bool skipped = skippedCount > 0; const int quoteDiffCount = max(0, getQuoteCount(word, outputLength) - - getQuoteCount(proximityInfoState->getPrimaryInputWord(), inputLength)); + - getQuoteCount(proximityInfoState->getPrimaryInputWord(), inputSize)); // TODO: Calculate edit distance for transposed and excessive int ed = 0; if (DEBUG_DICT_FULL) { - dumpEditDistance10ForDebug(editDistanceTable, correction->mInputLength, outputLength); + dumpEditDistance10ForDebug(editDistanceTable, correction->mInputSize, outputLength); } int adjustedProximityMatchedCount = proximityMatchedCount; int finalFreq = freq; if (DEBUG_CORRECTION_FREQ - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputLength)) { + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputSize)) { AKLOGI("FinalFreq0: %d", finalFreq); } // TODO: Optimize this. if (transposedCount > 0 || proximityMatchedCount > 0 || skipped || excessiveCount > 0) { - ed = getCurrentEditDistance(editDistanceTable, correction->mInputLength, outputLength, - inputLength) - transposedCount; + ed = getCurrentEditDistance(editDistanceTable, correction->mInputSize, outputLength, + inputSize) - transposedCount; const int matchWeight = powerIntCapped(typedLetterMultiplier, - max(inputLength, outputLength) - ed); + max(inputSize, outputLength) - ed); multiplyIntCapped(matchWeight, &finalFreq); // TODO: Demote further if there are two or more excessive chars with longer user input? - if (inputLength > outputLength) { + if (inputSize > outputLength) { multiplyRate(INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE, &finalFreq); } ed = max(0, ed - quoteDiffCount); - adjustedProximityMatchedCount = min(max(0, ed - (outputLength - inputLength)), + adjustedProximityMatchedCount = min(max(0, ed - (outputLength - inputSize)), proximityMatchedCount); if (transposedCount <= 0) { - if (ed == 1 && (inputLength == outputLength - 1 || inputLength == outputLength + 1)) { + if (ed == 1 && (inputSize == outputLength - 1 || inputSize == outputLength + 1)) { // Promote a word with just one skipped or excessive char if (sameLength) { multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE @@ -740,8 +740,8 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex // Demotion for a word with missing character if (skipped) { const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE - * (10 * inputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X) - / (10 * inputLength + * (10 * inputSize - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X) + / (10 * inputSize - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10); if (DEBUG_DICT_FULL) { AKLOGI("Demotion rate for missing character is %d.", demotionRate); @@ -843,7 +843,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex ? adjustedProximityMatchedCount : (proximityMatchedCount + transposedCount); multiplyRate( - 100 - CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE * errorCount / inputLength, &finalFreq); + 100 - CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE * errorCount / inputSize, &finalFreq); // Promotion for an exactly matched word if (ed == 0) { @@ -878,7 +878,7 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex e ... exceeding p ... proximity matching */ - if (matchCount == inputLength && matchCount >= 2 && !skipped + if (matchCount == inputSize && matchCount >= 2 && !skipped && word[matchCount] == word[matchCount - 1]) { multiplyRate(WORDS_WITH_MATCH_SKIP_PROMOTION_RATE, &finalFreq); } @@ -888,8 +888,8 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex multiplyIntCapped(fullWordMultiplier, &finalFreq); } - if (useFullEditDistance && outputLength > inputLength + 1) { - const int diff = outputLength - inputLength - 1; + if (useFullEditDistance && outputLength > inputSize + 1) { + const int diff = outputLength - inputSize - 1; const int divider = diff < 31 ? 1 << diff : S_INT_MAX; finalFreq = divider > finalFreq ? 1 : finalFreq / divider; } @@ -899,8 +899,8 @@ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex } if (DEBUG_CORRECTION_FREQ - && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputLength)) { - DUMP_WORD(correction->getPrimaryInputWord(), inputLength); + && (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputSize)) { + DUMP_WORD(correction->getPrimaryInputWord(), inputSize); DUMP_WORD(correction->mWord, outputLength); AKLOGI("FinalFreq: [P%d, S%d, T%d, E%d, A%d] %d, %d, %d, %d, %d, %d", proximityMatchedCount, skippedCount, transposedCount, excessiveCount, additionalProximityCount, diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h index 9899f6e66..c90c8d553 100644 --- a/native/jni/src/correction.h +++ b/native/jni/src/correction.h @@ -42,7 +42,7 @@ class Correction { virtual ~Correction(); void resetCorrection(); void initCorrection( - const ProximityInfo *pi, const int inputLength, const int maxWordLength); + const ProximityInfo *pi, const int inputSize, const int maxWordLength); void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll); // TODO: remove @@ -66,7 +66,7 @@ class Correction { const bool isSpaceProximity, const unsigned short *word); int getFinalProbability(const int probability, unsigned short **word, int *wordLength); int getFinalProbabilityForSubQueue(const int probability, unsigned short **word, - int *wordLength, const int inputLength); + int *wordLength, const int inputSize); CorrectionType processCharAndCalcState(const int32_t c, const bool isTerminal); @@ -90,7 +90,7 @@ class Correction { public: static int calculateFinalProbability(const int inputIndex, const int depth, const int probability, int *editDistanceTable, const Correction *correction, - const int inputLength); + const int inputSize); static int calcFreqForSplitMultipleWords(const int *freqArray, const int *wordLengthArray, const int wordCount, const Correction *correction, const bool isSpaceProximity, const unsigned short *word); @@ -105,9 +105,9 @@ class Correction { // proximity info state void initInputParams(const ProximityInfo *proximityInfo, const int32_t *inputCodes, - const int inputLength, const int *xCoordinates, const int *yCoordinates) { - mProximityInfoState.initInputParams( - proximityInfo, inputCodes, inputLength, xCoordinates, yCoordinates); + const int inputSize, const int *xCoordinates, const int *yCoordinates) { + mProximityInfoState.initInputParams(0, MAX_POINT_TO_KEY_LENGTH, + proximityInfo, inputCodes, inputSize, xCoordinates, yCoordinates, 0, 0, false); } const unsigned short *getPrimaryInputWord() const { @@ -204,7 +204,7 @@ class Correction { inline CorrectionType processUnrelatedCorrectionType(); inline void addCharToCurrentWord(const int32_t c); inline int getFinalProbabilityInternal(const int probability, unsigned short **word, - int *wordLength, const int inputLength); + int *wordLength, const int inputSize); static const int TYPED_LETTER_MULTIPLIER = 2; static const int FULL_WORD_MULTIPLIER = 2; @@ -214,7 +214,7 @@ class Correction { bool mDoAutoCompletion; int mMaxEditDistance; int mMaxDepth; - int mInputLength; + int mInputSize; int mSpaceProximityPos; int mMissingSpacePos; int mTerminalInputIndex; diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 929b303d6..9b530077a 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -294,6 +294,9 @@ static inline void prof_out(void) { #define MAX_SPACES_INTERNAL 16 +// Max Distance between point to key +#define MAX_POINT_TO_KEY_LENGTH 10000000 + // TODO: Reduce this constant if possible; check the maximum number of digraphs in the same // word in the dictionary for languages with digraphs, like German and French #define DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH 5 diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h index 146eb8055..f30e9fcc0 100644 --- a/native/jni/src/geometry_utils.h +++ b/native/jni/src/geometry_utils.h @@ -19,7 +19,6 @@ #include -#define MAX_DISTANCE 10000000 #define MAX_PATHS 2 #define DEBUG_DECODER false diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index 86c8a697a..0bb6dd9cb 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -20,13 +20,15 @@ #define LOG_TAG "LatinIME: proximity_info_state.cpp" #include "defines.h" +#include "geometry_utils.h" #include "proximity_info.h" #include "proximity_info_state.h" namespace latinime { -void ProximityInfoState::initInputParams( - const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputLength, - const int *xCoordinates, const int *yCoordinates) { +void ProximityInfoState::initInputParams(const int pointerId, const float maxLength, + const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, + const int *const xCoordinates, const int *const yCoordinates, const int *const times, + const int *const pointerIds, const bool isGeometric) { mProximityInfo = proximityInfo; mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData(); mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare(); @@ -45,7 +47,7 @@ void ProximityInfoState::initInputParams( memset(mInputCodes, 0, MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE_INTERNAL * sizeof(mInputCodes[0])); - for (int i = 0; i < inputLength; ++i) { + for (int i = 0; i < inputSize; ++i) { const int32_t primaryKey = inputCodes[i]; const int x = xCoordinates[i]; const int y = yCoordinates[i]; @@ -54,7 +56,7 @@ void ProximityInfoState::initInputParams( } if (DEBUG_PROXIMITY_CHARS) { - for (int i = 0; i < inputLength; ++i) { + for (int i = 0; i < inputSize; ++i) { AKLOGI("---"); for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) { int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; @@ -65,19 +67,57 @@ void ProximityInfoState::initInputParams( } } } - mInputXCoordinates = xCoordinates; - mInputYCoordinates = yCoordinates; - mTouchPositionCorrectionEnabled = - mHasTouchPositionCorrectionData && xCoordinates && yCoordinates; - mInputLength = inputLength; - for (int i = 0; i < inputLength; ++i) { + + mMaxPointToKeyLength = maxLength; + /////////////////////// + // Setup touch points + mInputXs.clear(); + mInputYs.clear(); + mTimes.clear(); + mLengthCache.clear(); + mDistanceCache.clear(); + + mInputSize = 0; + if (xCoordinates && yCoordinates) { + const bool proximityOnly = !isGeometric && (xCoordinates[0] < 0 || yCoordinates[0] < 0); + for (int i = 0; i < inputSize; ++i) { + ++mInputSize; + // Assuming pointerId == 0 if pointerIds is null. + const int pid = pointerIds ? pointerIds[i] : 0; + if (pointerId == pid) { + const int c = isGeometric ? NOT_A_COORDINATE : getPrimaryCharAt(i); + const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i]; + const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i]; + const int time = times ? times[i] : -1; + pushTouchPoint(c, x, y, time, isGeometric); + } + } + } + + if (mInputSize > 0) { + const int keyCount = mProximityInfo->getKeyCount(); + mDistanceCache.resize(mInputSize * keyCount); + for (int i = 0; i < mInputSize; ++i) { + for (int k = 0; k < keyCount; ++k) { + const int index = i * keyCount + k; + const int x = mInputXs[i]; + const int y = mInputYs[i]; + mDistanceCache[index] = + mProximityInfo->getNormalizedSquaredDistanceFromCenterFloat(k, x, y); + } + } + } + // end + /////////////////////// + + for (int i = 0; i < inputSize; ++i) { mPrimaryInputWord[i] = getPrimaryCharAt(i); } - mPrimaryInputWord[inputLength] = 0; - if (DEBUG_PROXIMITY_CHARS) { - AKLOGI("--- initInputParams"); - } - for (int i = 0; i < mInputLength; ++i) { + mPrimaryInputWord[inputSize] = 0; + + mTouchPositionCorrectionEnabled = + mHasTouchPositionCorrectionData && xCoordinates && yCoordinates && !isGeometric; + for (int i = 0; i < mInputSize && mTouchPositionCorrectionEnabled; ++i) { const int *proximityChars = getProximityCharsAt(i); const int primaryKey = proximityChars[0]; const int x = xCoordinates[i]; @@ -108,6 +148,32 @@ void ProximityInfoState::initInputParams( } } +void ProximityInfoState::pushTouchPoint(const int nodeChar, int x, int y, + const int time, const bool sample) { + const uint32_t size = mInputXs.size(); + // TODO: Should have a const variable for 10 + const int sampleRate = mProximityInfo->getMostCommonKeyWidth() / 10; + if (size > 0) { + const int dist = getDistanceInt(x, y, mInputXs[size - 1], mInputYs[size - 1]); + if (sample && dist < sampleRate) { + return; + } + mLengthCache.push_back(mLengthCache[size - 1] + dist); + } else { + mLengthCache.push_back(0); + } + if (nodeChar >= 0 && (x < 0 || y < 0)) { + const int keyId = mProximityInfo->getKeyIndex(nodeChar); + if (keyId >= 0) { + x = mProximityInfo->getKeyCenterXOfIdG(keyId); + y = mProximityInfo->getKeyCenterYOfIdG(keyId); + } + } + mInputXs.push_back(x); + mInputYs.push_back(y); + mTimes.push_back(time); +} + float ProximityInfoState::calculateNormalizedSquaredDistance( const int keyIndex, const int inputIndex) const { if (keyIndex == NOT_AN_INDEX) { @@ -116,7 +182,7 @@ float ProximityInfoState::calculateNormalizedSquaredDistance( if (!mProximityInfo->hasSweetSpotData(keyIndex)) { return NOT_A_DISTANCE_FLOAT; } - if (NOT_A_COORDINATE == mInputXCoordinates[inputIndex]) { + if (NOT_A_COORDINATE == mInputXs[inputIndex]) { return NOT_A_DISTANCE_FLOAT; } const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter( @@ -125,12 +191,37 @@ float ProximityInfoState::calculateNormalizedSquaredDistance( return squaredDistance / squaredRadius; } +int ProximityInfoState::getDuration(const int index) const { + if (mTimes.size() == 0 || index <= 0 || index >= static_cast(mInputSize) - 1) { + return 0; + } + return mTimes[index + 1] - mTimes[index - 1]; +} + +float ProximityInfoState::getPointToKeyLength(int inputIndex, int charCode, float scale) { + const int keyId = mProximityInfo->getKeyIndex(charCode); + if (keyId >= 0) { + const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; + return min(mDistanceCache[index] * scale, mMaxPointToKeyLength); + } + return 0; +} + +int ProximityInfoState::getKeyKeyDistance(int key0, int key1) { + return mProximityInfo->getKeyKeyDistanceG(key0, key1); +} + +int ProximityInfoState::getSpaceY() { + const int keyId = mProximityInfo->getKeyIndex(' '); + return mProximityInfo->getKeyCenterYOfIdG(keyId); +} + float ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter( const int keyIndex, const int inputIndex) const { const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex); const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex); - const float inputX = static_cast(mInputXCoordinates[inputIndex]); - const float inputY = static_cast(mInputYCoordinates[inputIndex]); + const float inputX = static_cast(mInputXs[inputIndex]); + const float inputY = static_cast(mInputYs[inputIndex]); return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY); } } // namespace latinime diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 2fb0ae044..81d68fdcb 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -19,6 +19,7 @@ #include #include +#include #include "char_utils.h" #include "defines.h" @@ -40,9 +41,10 @@ class ProximityInfoState { ///////////////////////////////////////// // Defined in proximity_info_state.cpp // ///////////////////////////////////////// - void initInputParams( - const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputLength, - const int *xCoordinates, const int *yCoordinates); + void initInputParams(const int pointerId, const float maxLength, + const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, + const int *xCoordinates, const int *yCoordinates, const int *const times, + const int *const pointerIds, const bool isGeometric); ///////////////////////////////////////// // Defined here // @@ -65,14 +67,14 @@ class ProximityInfoState { } inline bool existsAdjacentProximityChars(const int index) const { - if (index < 0 || index >= mInputLength) return false; + if (index < 0 || index >= mInputSize) return false; const int currentChar = getPrimaryCharAt(index); const int leftIndex = index - 1; if (leftIndex >= 0 && existsCharInProximityAt(leftIndex, currentChar)) { return true; } const int rightIndex = index + 1; - if (rightIndex < mInputLength && existsCharInProximityAt(rightIndex, currentChar)) { + if (rightIndex < mInputSize && existsCharInProximityAt(rightIndex, currentChar)) { return true; } return false; @@ -158,7 +160,7 @@ class ProximityInfoState { } inline bool sameAsTyped(const unsigned short *word, int length) const { - if (length != mInputLength) { + if (length != mInputSize) { return false; } const int *inputCodes = mInputCodes; @@ -172,6 +174,18 @@ class ProximityInfoState { return true; } + int getDuration(const int index) const; + + bool isUsed() const { + return mInputSize > 0; + } + + float getPointToKeyLength(int inputIndex, int charCode, float scale); + + int getKeyKeyDistance(int key0, int key1); + + int getSpaceY(); + private: DISALLOW_COPY_AND_ASSIGN(ProximityInfoState); ///////////////////////////////////////// @@ -182,13 +196,14 @@ class ProximityInfoState { float calculateSquaredDistanceFromSweetSpotCenter( const int keyIndex, const int inputIndex) const; + void pushTouchPoint(const int nodeChar, int x, int y, const int time, const bool sample); ///////////////////////////////////////// // Defined here // ///////////////////////////////////////// inline float square(const float x) const { return x * x; } bool hasInputCoordinates() const { - return mInputXCoordinates && mInputYCoordinates; + return mInputXs.size() > 0 && mInputYs.size() > 0; } inline const int *getProximityCharsAt(const int index) const { @@ -197,6 +212,7 @@ class ProximityInfoState { // const const ProximityInfo *mProximityInfo; + float mMaxPointToKeyLength; bool mHasTouchPositionCorrectionData; int mMostCommonKeyWidthSquare; std::string mLocaleStr; @@ -206,12 +222,15 @@ class ProximityInfoState { int mGridHeight; int mGridWidth; - const int *mInputXCoordinates; - const int *mInputYCoordinates; + std::vector mInputXs; + std::vector mInputYs; + std::vector mTimes; + std::vector mDistanceCache; + std::vector mLengthCache; bool mTouchPositionCorrectionEnabled; int32_t mInputCodes[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL]; int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL]; - int mInputLength; + int mInputSize; unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL]; }; } // namespace latinime diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index 705defc00..ba3c2db6b 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -237,7 +237,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, - const int inputLength, const std::map *bigramMap, const uint8_t *bigramFilter, + const int inputSize, const std::map *bigramMap, const uint8_t *bigramFilter, const bool useFullEditDistance, Correction *correction, WordsPriorityQueuePool *queuePool) const { @@ -247,7 +247,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, PROF_START(1); getOneWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, bigramMap, bigramFilter, - useFullEditDistance, inputLength, correction, queuePool); + useFullEditDistance, inputSize, correction, queuePool); PROF_END(1); PROF_START(2); @@ -263,7 +263,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, WordsPriorityQueue *masterQueue = queuePool->getMasterQueue(); if (masterQueue->size() > 0) { float nsForMaster = masterQueue->getHighestNormalizedScore( - correction->getPrimaryInputWord(), inputLength, 0, 0, 0); + correction->getPrimaryInputWord(), inputSize, 0, 0, 0); hasAutoCorrectionCandidate = (nsForMaster > START_TWO_WORDS_CORRECTION_THRESHOLD); } PROF_END(4); @@ -271,9 +271,9 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, PROF_START(5); // Multiple word suggestions if (SUGGEST_MULTIPLE_WORDS - && inputLength >= MIN_USER_TYPED_LENGTH_FOR_MULTIPLE_WORD_SUGGESTION) { + && inputSize >= MIN_USER_TYPED_LENGTH_FOR_MULTIPLE_WORD_SUGGESTION) { getSplitMultipleWordsSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, - useFullEditDistance, inputLength, correction, queuePool, + useFullEditDistance, inputSize, correction, queuePool, hasAutoCorrectionCandidate); } PROF_END(5); @@ -304,15 +304,15 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, } void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xCoordinates, - const int *yCoordinates, const int *codes, const int inputLength, + const int *yCoordinates, const int *codes, const int inputSize, Correction *correction) const { if (DEBUG_DICT) { AKLOGI("initSuggest"); - DUMP_WORD_INT(codes, inputLength); + DUMP_WORD_INT(codes, inputSize); } - correction->initInputParams(proximityInfo, codes, inputLength, xCoordinates, yCoordinates); - const int maxDepth = min(inputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH); - correction->initCorrection(proximityInfo, inputLength, maxDepth); + correction->initInputParams(proximityInfo, codes, inputSize, xCoordinates, yCoordinates); + const int maxDepth = min(inputSize * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH); + correction->initCorrection(proximityInfo, inputSize, maxDepth); } static const char QUOTE = '\''; @@ -321,15 +321,15 @@ static const char SPACE = ' '; void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const std::map *bigramMap, const uint8_t *bigramFilter, - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool) const { - initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputLength, correction); - getSuggestionCandidates(useFullEditDistance, inputLength, bigramMap, bigramFilter, correction, + initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputSize, correction); + getSuggestionCandidates(useFullEditDistance, inputSize, bigramMap, bigramFilter, correction, queuePool, true /* doAutoCompletion */, DEFAULT_MAX_ERRORS, FIRST_WORD_INDEX); } void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance, - const int inputLength, const std::map *bigramMap, const uint8_t *bigramFilter, + const int inputSize, const std::map *bigramMap, const uint8_t *bigramFilter, Correction *correction, WordsPriorityQueuePool *queuePool, const bool doAutoCompletion, const int maxErrors, const int currentWordIndex) const { uint8_t totalTraverseCount = correction->pushAndGetTotalTraverseCount(); @@ -351,7 +351,7 @@ void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance, int childCount = BinaryFormat::getGroupCountAndForwardPointer(DICT_ROOT, &rootPosition); int outputIndex = 0; - correction->initCorrectionState(rootPosition, childCount, (inputLength <= 0)); + correction->initCorrectionState(rootPosition, childCount, (inputSize <= 0)); // Depth first search while (outputIndex >= 0) { @@ -446,7 +446,7 @@ inline void UnigramDictionary::onTerminal(const int probability, int UnigramDictionary::getSubStringSuggestion( ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const bool useFullEditDistance, Correction *correction, - WordsPriorityQueuePool *queuePool, const int inputLength, + WordsPriorityQueuePool *queuePool, const int inputSize, const bool hasAutoCorrectionCandidate, const int currentWordIndex, const int inputWordStartPos, const int inputWordLength, const int outputWordStartPos, const bool isSpaceProximity, int *freqArray, @@ -497,7 +497,7 @@ int UnigramDictionary::getSubStringSuggestion( int nextWordLength = 0; // TODO: Optimize init suggestion initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, - inputLength, correction); + inputSize, correction); unsigned short word[MAX_WORD_LENGTH_INTERNAL]; int freq = getMostFrequentWordLike( @@ -566,7 +566,7 @@ int UnigramDictionary::getSubStringSuggestion( *outputWordLength = tempOutputWordLength; } - if ((inputWordStartPos + inputWordLength) < inputLength) { + if ((inputWordStartPos + inputWordLength) < inputSize) { if (outputWordStartPos + nextWordLength >= MAX_WORD_LENGTH) { return FLAG_MULTIPLE_SUGGEST_SKIP; } @@ -585,7 +585,7 @@ int UnigramDictionary::getSubStringSuggestion( freqArray[i], wordLengthArray[i]); } AKLOGI("Split two words: freq = %d, length = %d, %d, isSpace ? %d", pairFreq, - inputLength, tempOutputWordLength, isSpaceProximity); + inputSize, tempOutputWordLength, isSpaceProximity); } addWord(outputWord, tempOutputWordLength, pairFreq, queuePool->getMasterQueue(), Dictionary::KIND_CORRECTION); @@ -595,7 +595,7 @@ int UnigramDictionary::getSubStringSuggestion( void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool, const bool hasAutoCorrectionCandidate, const int startInputPos, const int startWordIndex, const int outputWordLength, int *freqArray, int *wordLengthArray, @@ -606,11 +606,11 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, } if (startWordIndex >= 1 && (hasAutoCorrectionCandidate - || inputLength < MIN_INPUT_LENGTH_FOR_THREE_OR_MORE_WORDS_CORRECTION)) { + || inputSize < MIN_INPUT_LENGTH_FOR_THREE_OR_MORE_WORDS_CORRECTION)) { // Do not suggest 3+ words if already has auto correction candidate return; } - for (int i = startInputPos + 1; i < inputLength; ++i) { + for (int i = startInputPos + 1; i < inputSize; ++i) { if (DEBUG_CORRECTION_FREQ) { AKLOGI("Multi words(%d), start in %d sep %d start out %d", startWordIndex, startInputPos, i, outputWordLength); @@ -621,7 +621,7 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, int inputWordStartPos = startInputPos; int inputWordLength = i - startInputPos; const int suggestionFlag = getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, - codes, useFullEditDistance, correction, queuePool, inputLength, + codes, useFullEditDistance, correction, queuePool, inputSize, hasAutoCorrectionCandidate, startWordIndex, inputWordStartPos, inputWordLength, outputWordLength, true /* not used */, freqArray, wordLengthArray, outputWord, &tempOutputWordLength); @@ -638,14 +638,14 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, // Next word // Missing space inputWordStartPos = i; - inputWordLength = inputLength - i; + inputWordLength = inputSize - i; if(getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes, - useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate, + useFullEditDistance, correction, queuePool, inputSize, hasAutoCorrectionCandidate, startWordIndex + 1, inputWordStartPos, inputWordLength, tempOutputWordLength, false /* missing space */, freqArray, wordLengthArray, outputWord, 0) != FLAG_MULTIPLE_SUGGEST_CONTINUE) { getMultiWordsSuggestionRec(proximityInfo, xcoordinates, ycoordinates, codes, - useFullEditDistance, inputLength, correction, queuePool, + useFullEditDistance, inputSize, correction, queuePool, hasAutoCorrectionCandidate, inputWordStartPos, startWordIndex + 1, tempOutputWordLength, freqArray, wordLengthArray, outputWord); } @@ -668,7 +668,7 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, AKLOGI("Do mistyped space correction"); } getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes, - useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate, + useFullEditDistance, correction, queuePool, inputSize, hasAutoCorrectionCandidate, startWordIndex + 1, inputWordStartPos, inputWordLength, tempOutputWordLength, true /* mistyped space */, freqArray, wordLengthArray, outputWord, 0); } @@ -676,10 +676,10 @@ void UnigramDictionary::getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, void UnigramDictionary::getSplitMultipleWordsSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool, const bool hasAutoCorrectionCandidate) const { - if (inputLength >= MAX_WORD_LENGTH) return; + if (inputSize >= MAX_WORD_LENGTH) return; if (DEBUG_DICT) { AKLOGI("--- Suggest multiple words"); } @@ -692,7 +692,7 @@ void UnigramDictionary::getSplitMultipleWordsSuggestions(ProximityInfo *proximit const int startInputPos = 0; const int startWordIndex = 0; getMultiWordsSuggestionRec(proximityInfo, xcoordinates, ycoordinates, codes, - useFullEditDistance, inputLength, correction, queuePool, hasAutoCorrectionCandidate, + useFullEditDistance, inputSize, correction, queuePool, hasAutoCorrectionCandidate, startInputPos, startWordIndex, outputWordLength, freqArray, wordLengthArray, outputWord); } @@ -700,13 +700,13 @@ void UnigramDictionary::getSplitMultipleWordsSuggestions(ProximityInfo *proximit // Wrapper for getMostFrequentWordLikeInner, which matches it to the previous // interface. inline int UnigramDictionary::getMostFrequentWordLike(const int startInputIndex, - const int inputLength, Correction *correction, unsigned short *word) const { - uint16_t inWord[inputLength]; + const int inputSize, Correction *correction, unsigned short *word) const { + uint16_t inWord[inputSize]; - for (int i = 0; i < inputLength; ++i) { + for (int i = 0; i < inputSize; ++i) { inWord[i] = (uint16_t)correction->getPrimaryCharAt(startInputIndex + i); } - return getMostFrequentWordLikeInner(inWord, inputLength, word); + return getMostFrequentWordLikeInner(inWord, inputSize, word); } // This function will take the position of a character array within a CharGroup, diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h index 6083f0175..2c6622210 100644 --- a/native/jni/src/unigram_dictionary.h +++ b/native/jni/src/unigram_dictionary.h @@ -53,7 +53,7 @@ class UnigramDictionary { private: DISALLOW_IMPLICIT_CONSTRUCTORS(UnigramDictionary); void getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, - const int *ycoordinates, const int *codes, const int inputLength, + const int *ycoordinates, const int *codes, const int inputSize, const std::map *bigramMap, const uint8_t *bigramFilter, const bool useFullEditDistance, Correction *correction, WordsPriorityQueuePool *queuePool) const; @@ -72,16 +72,16 @@ class UnigramDictionary { Correction *correction) const; void getOneWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const std::map *bigramMap, - const uint8_t *bigramFilter, const bool useFullEditDistance, const int inputLength, + const uint8_t *bigramFilter, const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool) const; void getSuggestionCandidates( - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, const std::map *bigramMap, const uint8_t *bigramFilter, Correction *correction, WordsPriorityQueuePool *queuePool, const bool doAutoCompletion, const int maxErrors, const int currentWordIndex) const; void getSplitMultipleWordsSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool, const bool hasAutoCorrectionCandidate) const; void onTerminal(const int freq, const TerminalAttributes& terminalAttributes, @@ -92,21 +92,21 @@ class UnigramDictionary { const uint8_t *bigramFilter, Correction *correction, int *newCount, int *newChildPosition, int *nextSiblingPosition, WordsPriorityQueuePool *queuePool, const int currentWordIndex) const; - int getMostFrequentWordLike(const int startInputIndex, const int inputLength, + int getMostFrequentWordLike(const int startInputIndex, const int inputSize, Correction *correction, unsigned short *word) const; int getMostFrequentWordLikeInner(const uint16_t *const inWord, const int length, short unsigned int *outWord) const; int getSubStringSuggestion( ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const bool useFullEditDistance, Correction *correction, - WordsPriorityQueuePool *queuePool, const int inputLength, + WordsPriorityQueuePool *queuePool, const int inputSize, const bool hasAutoCorrectionCandidate, const int currentWordIndex, const int inputWordStartPos, const int inputWordLength, const int outputWordStartPos, const bool isSpaceProximity, int *freqArray, int *wordLengthArray, unsigned short *outputWord, int *outputWordLength) const; void getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, - const bool useFullEditDistance, const int inputLength, + const bool useFullEditDistance, const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool, const bool hasAutoCorrectionCandidate, const int startPos, const int startWordIndex, const int outputWordLength, int *freqArray, int *wordLengthArray, From be8a4cca2b021c5e8d79781f7a5d175f8b3b9976 Mon Sep 17 00:00:00 2001 From: Ariel Bernal Date: Tue, 14 Aug 2012 16:06:07 -0400 Subject: [PATCH 114/133] Fixing unused private field error Fixing error resulting from Wunused-private-field. Change-Id: I2bbdefa707a55564eba9b77a88e229921787f357 Author: Ariel Bernal Reviewed-by: Tareq A Siraj --- native/jni/src/proximity_info.cpp | 4 ++-- native/jni/src/proximity_info.h | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 960d40119..855619dd7 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -42,8 +42,8 @@ ProximityInfo::ProximityInfo(const std::string localeStr, const int maxProximity const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights, const int32_t *keyCharCodes, const float *sweetSpotCenterXs, const float *sweetSpotCenterYs, const float *sweetSpotRadii) - : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), - KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), + : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), + GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index feb0c9444..80d43af9d 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -98,8 +98,6 @@ class ProximityInfo { const int x, const int y, const int32_t primaryKey, int *inputCodes) const; const int MAX_PROXIMITY_CHARS_SIZE; - const int KEYBOARD_WIDTH; - const int KEYBOARD_HEIGHT; const int GRID_WIDTH; const int GRID_HEIGHT; const int MOST_COMMON_KEY_WIDTH_SQUARE; @@ -108,8 +106,6 @@ class ProximityInfo { const int KEY_COUNT; const bool HAS_TOUCH_POSITION_CORRECTION_DATA; const std::string mLocaleStr; - // TODO: remove this - const int *mInputCodesFromJava; int32_t *mInputCodes; const int *mInputXCoordinates; const int *mInputYCoordinates; From dd70f8e8b18c2a56604ef114d4847d3366530ce6 Mon Sep 17 00:00:00 2001 From: Ariel Bernal Date: Mon, 13 Aug 2012 12:07:35 -0400 Subject: [PATCH 115/133] Fixed inline initialization of non-integral static const member Such initializations are a GNU extension not supported by clang. Change-Id: I543c35cacdda080d2599cb99587661b2e9cd404f Author: Ariel Bernal Reviewed-by: Kevin P Schoedel --- native/jni/src/proximity_info.cpp | 2 ++ native/jni/src/proximity_info.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 960d40119..6b83600a0 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -35,6 +35,8 @@ inline void copyOrFillZero(void *to, const void *from, size_t size) { } } +const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f; + ProximityInfo::ProximityInfo(const std::string localeStr, const int maxProximityCharsSize, const int keyboardWidth, const int keyboardHeight, const int gridWidth, const int gridHeight, const int mostCommonKeyWidth, diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index feb0c9444..d4d8f4494 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -77,7 +77,7 @@ class ProximityInfo { static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64; // The upper limit of the char code in mCodeToKeyIndex static const int MAX_CHAR_CODE = 127; - static const float NOT_A_DISTANCE_FLOAT = -1.0f; + static const float NOT_A_DISTANCE_FLOAT; static const int NOT_A_CODE = -1; int getStartIndexFromCoordinates(const int x, const int y) const; From d4fe7fda303ff937d2e44c15dde9d90cbf59376b Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Tue, 21 Aug 2012 18:30:18 +0900 Subject: [PATCH 116/133] Use ByteBuffer when reading FusionDictionary from file. Change-Id: Ia71561648e17f846d277c22309ac37c21c67a537 --- .../latin/BinaryDictionaryGetter.java | 27 ++- .../latin/makedict/BinaryDictInputOutput.java | 225 +++++++++++------- .../inputmethod/latin/BinaryDictIOTests.java | 224 +++++++++++++++++ .../latin/dicttool/DictionaryMaker.java | 22 +- 4 files changed, 399 insertions(+), 99 deletions(-) create mode 100644 tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 4ada909de..e1cb195bc 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -25,7 +25,10 @@ import android.content.res.AssetFileDescriptor; import android.util.Log; import java.io.File; -import java.io.RandomAccessFile; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; @@ -349,17 +352,21 @@ class BinaryDictionaryGetter { // ad-hock ## HACK ## if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true; + FileInputStream inStream = null; try { // Read the version of the file - final RandomAccessFile raf = new RandomAccessFile(f, "r"); - final int magic = raf.readInt(); + inStream = new FileInputStream(f); + final ByteBuffer buffer = inStream.getChannel().map( + FileChannel.MapMode.READ_ONLY, 0, f.length()); + final int magic = buffer.getInt(); if (magic != BinaryDictInputOutput.VERSION_2_MAGIC_NUMBER) { return false; } - final int formatVersion = raf.readInt(); - final int headerSize = raf.readInt(); + final int formatVersion = buffer.getInt(); + final int headerSize = buffer.getInt(); final HashMap options = CollectionUtils.newHashMap(); - BinaryDictInputOutput.populateOptionsFromFile(raf, headerSize, options); + BinaryDictInputOutput.populateOptions(buffer, headerSize, options); + final String version = options.get(VERSION_KEY); if (null == version) { // No version in the options : the format is unexpected @@ -374,6 +381,14 @@ class BinaryDictionaryGetter { return false; } catch (NumberFormatException e) { return false; + } finally { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + // do nothing + } + } } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index b23b7db34..161b94ca0 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -22,10 +22,13 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.Node; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; -import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -307,33 +310,32 @@ public class BinaryDictInputOutput { } /** - * Reads a string from a RandomAccessFile. This is the converse of the above method. + * Reads a string from a ByteBuffer. This is the converse of the above method. */ - private static String readString(final RandomAccessFile source) throws IOException { + private static String readString(final ByteBuffer buffer) { final StringBuilder s = new StringBuilder(); - int character = readChar(source); + int character = readChar(buffer); while (character != INVALID_CHARACTER) { s.appendCodePoint(character); - character = readChar(source); + character = readChar(buffer); } return s.toString(); } /** - * Reads a character from the file. + * Reads a character from the ByteBuffer. * * This follows the character format documented earlier in this source file. * - * @param source the file, positioned over an encoded character. + * @param buffer the buffer, positioned over an encoded character. * @return the character code. */ - private static int readChar(RandomAccessFile source) throws IOException { - int character = source.readUnsignedByte(); + private static int readChar(final ByteBuffer buffer) { + int character = readUnsignedByte(buffer); if (!fitsOnOneByte(character)) { - if (GROUP_CHARACTERS_TERMINATOR == character) - return INVALID_CHARACTER; + if (GROUP_CHARACTERS_TERMINATOR == character) return INVALID_CHARACTER; character <<= 16; - character += source.readUnsignedShort(); + character += readUnsignedShort(buffer); } return character; } @@ -1091,46 +1093,46 @@ public class BinaryDictInputOutput { // readDictionaryBinary is the public entry point for them. static final int[] characterBuffer = new int[MAX_WORD_LENGTH]; - private static CharGroupInfo readCharGroup(RandomAccessFile source, - final int originalGroupAddress) throws IOException { + private static CharGroupInfo readCharGroup(final ByteBuffer buffer, + final int originalGroupAddress) { int addressPointer = originalGroupAddress; - final int flags = source.readUnsignedByte(); + final int flags = readUnsignedByte(buffer); ++addressPointer; final int characters[]; if (0 != (flags & FLAG_HAS_MULTIPLE_CHARS)) { int index = 0; - int character = CharEncoding.readChar(source); + int character = CharEncoding.readChar(buffer); addressPointer += CharEncoding.getCharSize(character); while (-1 != character) { characterBuffer[index++] = character; - character = CharEncoding.readChar(source); + character = CharEncoding.readChar(buffer); addressPointer += CharEncoding.getCharSize(character); } characters = Arrays.copyOfRange(characterBuffer, 0, index); } else { - final int character = CharEncoding.readChar(source); + final int character = CharEncoding.readChar(buffer); addressPointer += CharEncoding.getCharSize(character); characters = new int[] { character }; } final int frequency; if (0 != (FLAG_IS_TERMINAL & flags)) { ++addressPointer; - frequency = source.readUnsignedByte(); + frequency = readUnsignedByte(buffer); } else { frequency = CharGroup.NOT_A_TERMINAL; } int childrenAddress = addressPointer; switch (flags & MASK_GROUP_ADDRESS_TYPE) { case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE: - childrenAddress += source.readUnsignedByte(); + childrenAddress += readUnsignedByte(buffer); addressPointer += 1; break; case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES: - childrenAddress += source.readUnsignedShort(); + childrenAddress += readUnsignedShort(buffer); addressPointer += 2; break; case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES: - childrenAddress += (source.readUnsignedByte() << 16) + source.readUnsignedShort(); + childrenAddress += readUnsignedInt24(buffer); addressPointer += 3; break; case FLAG_GROUP_ADDRESS_TYPE_NOADDRESS: @@ -1140,38 +1142,38 @@ public class BinaryDictInputOutput { } ArrayList shortcutTargets = null; if (0 != (flags & FLAG_HAS_SHORTCUT_TARGETS)) { - final long pointerBefore = source.getFilePointer(); + final int pointerBefore = buffer.position(); shortcutTargets = new ArrayList(); - source.readUnsignedShort(); // Skip the size + buffer.getShort(); // Skip the size while (true) { - final int targetFlags = source.readUnsignedByte(); - final String word = CharEncoding.readString(source); + final int targetFlags = readUnsignedByte(buffer); + final String word = CharEncoding.readString(buffer); shortcutTargets.add(new WeightedString(word, targetFlags & FLAG_ATTRIBUTE_FREQUENCY)); if (0 == (targetFlags & FLAG_ATTRIBUTE_HAS_NEXT)) break; } - addressPointer += (source.getFilePointer() - pointerBefore); + addressPointer += buffer.position() - pointerBefore; } ArrayList bigrams = null; if (0 != (flags & FLAG_HAS_BIGRAMS)) { bigrams = new ArrayList(); while (true) { - final int bigramFlags = source.readUnsignedByte(); + final int bigramFlags = readUnsignedByte(buffer); ++addressPointer; final int sign = 0 == (bigramFlags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) ? 1 : -1; int bigramAddress = addressPointer; switch (bigramFlags & MASK_ATTRIBUTE_ADDRESS_TYPE) { case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: - bigramAddress += sign * source.readUnsignedByte(); + bigramAddress += sign * readUnsignedByte(buffer); addressPointer += 1; break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: - bigramAddress += sign * source.readUnsignedShort(); + bigramAddress += sign * readUnsignedShort(buffer); addressPointer += 2; break; case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: - final int offset = ((source.readUnsignedByte() << 16) - + source.readUnsignedShort()); + final int offset = (readUnsignedByte(buffer) << 16) + + readUnsignedShort(buffer); bigramAddress += sign * offset; addressPointer += 3; break; @@ -1188,15 +1190,15 @@ public class BinaryDictInputOutput { } /** - * Reads and returns the char group count out of a file and forwards the pointer. + * Reads and returns the char group count out of a buffer and forwards the pointer. */ - private static int readCharGroupCount(RandomAccessFile source) throws IOException { - final int msb = source.readUnsignedByte(); + private static int readCharGroupCount(final ByteBuffer buffer) { + final int msb = readUnsignedByte(buffer); if (MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) { return msb; } else { return ((MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8) - + source.readUnsignedByte(); + + readUnsignedByte(buffer); } } @@ -1204,31 +1206,29 @@ public class BinaryDictInputOutput { // of this method. Since it performs direct, unbuffered random access to the file and // may be called hundreds of thousands of times, the resulting performance is not // reasonable without some kind of cache. Thus: - // TODO: perform buffered I/O here and in other places in the code. private static TreeMap wordCache = new TreeMap(); /** * Finds, as a string, the word at the address passed as an argument. * - * @param source the file to read from. + * @param buffer the buffer to read from. * @param headerSize the size of the header. * @param address the address to seek. * @return the word, as a string. - * @throws IOException if the file can't be read. */ - private static String getWordAtAddress(final RandomAccessFile source, final long headerSize, - int address) throws IOException { + private static String getWordAtAddress(final ByteBuffer buffer, final int headerSize, + final int address) { final String cachedString = wordCache.get(address); if (null != cachedString) return cachedString; - final long originalPointer = source.getFilePointer(); - source.seek(headerSize); - final int count = readCharGroupCount(source); + final int originalPointer = buffer.position(); + buffer.position(headerSize); + final int count = readCharGroupCount(buffer); int groupOffset = getGroupCountSize(count); final StringBuilder builder = new StringBuilder(); String result = null; CharGroupInfo last = null; for (int i = count - 1; i >= 0; --i) { - CharGroupInfo info = readCharGroup(source, groupOffset); + CharGroupInfo info = readCharGroup(buffer, groupOffset); groupOffset = info.mEndAddress; if (info.mOriginalAddress == address) { builder.append(new String(info.mCharacters, 0, info.mCharacters.length)); @@ -1239,9 +1239,9 @@ public class BinaryDictInputOutput { if (info.mChildrenAddress > address) { if (null == last) continue; builder.append(new String(last.mCharacters, 0, last.mCharacters.length)); - source.seek(last.mChildrenAddress + headerSize); + buffer.position(last.mChildrenAddress + headerSize); groupOffset = last.mChildrenAddress + 1; - i = source.readUnsignedByte(); + i = readUnsignedByte(buffer); last = null; continue; } @@ -1249,14 +1249,14 @@ public class BinaryDictInputOutput { } if (0 == i && hasChildrenAddress(last.mChildrenAddress)) { builder.append(new String(last.mCharacters, 0, last.mCharacters.length)); - source.seek(last.mChildrenAddress + headerSize); + buffer.position(last.mChildrenAddress + headerSize); groupOffset = last.mChildrenAddress + 1; - i = source.readUnsignedByte(); + i = readUnsignedByte(buffer); last = null; continue; } } - source.seek(originalPointer); + buffer.position(originalPointer); wordCache.put(address, result); return result; } @@ -1269,44 +1269,47 @@ public class BinaryDictInputOutput { * This will recursively read other nodes into the structure, populating the reverse * maps on the fly and using them to keep track of already read nodes. * - * @param source the data file, correctly positioned at the start of a node. + * @param buffer the buffer, correctly positioned at the start of a node. * @param headerSize the size, in bytes, of the file header. * @param reverseNodeMap a mapping from addresses to already read nodes. * @param reverseGroupMap a mapping from addresses to already read character groups. * @return the read node with all his children already read. */ - private static Node readNode(RandomAccessFile source, long headerSize, - Map reverseNodeMap, Map reverseGroupMap) + private static Node readNode(final ByteBuffer buffer, final int headerSize, + final Map reverseNodeMap, final Map reverseGroupMap) throws IOException { - final int nodeOrigin = (int)(source.getFilePointer() - headerSize); - final int count = readCharGroupCount(source); + final int nodeOrigin = buffer.position() - headerSize; + final int count = readCharGroupCount(buffer); final ArrayList nodeContents = new ArrayList(); int groupOffset = nodeOrigin + getGroupCountSize(count); for (int i = count; i > 0; --i) { - CharGroupInfo info = readCharGroup(source, groupOffset); + CharGroupInfo info =readCharGroup(buffer, groupOffset); ArrayList shortcutTargets = info.mShortcutTargets; ArrayList bigrams = null; if (null != info.mBigrams) { bigrams = new ArrayList(); for (PendingAttribute bigram : info.mBigrams) { - final String word = getWordAtAddress(source, headerSize, bigram.mAddress); + final String word = getWordAtAddress( + buffer, headerSize, bigram.mAddress); bigrams.add(new WeightedString(word, bigram.mFrequency)); } } if (hasChildrenAddress(info.mChildrenAddress)) { Node children = reverseNodeMap.get(info.mChildrenAddress); if (null == children) { - final long currentPosition = source.getFilePointer(); - source.seek(info.mChildrenAddress + headerSize); - children = readNode(source, headerSize, reverseNodeMap, reverseGroupMap); - source.seek(currentPosition); + final int currentPosition = buffer.position(); + buffer.position(info.mChildrenAddress + headerSize); + children = readNode( + buffer, headerSize, reverseNodeMap, reverseGroupMap); + buffer.position(currentPosition); } nodeContents.add( - new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency, - children)); + new CharGroup(info.mCharacters, shortcutTargets, + bigrams, info.mFrequency, children)); } else { nodeContents.add( - new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency)); + new CharGroup(info.mCharacters, shortcutTargets, + bigrams, info.mFrequency)); } groupOffset = info.mEndAddress; } @@ -1318,12 +1321,13 @@ public class BinaryDictInputOutput { /** * Helper function to get the binary format version from the header. + * @throws IOException */ - private static int getFormatVersion(final RandomAccessFile source) throws IOException { - final int magic_v1 = source.readUnsignedShort(); - if (VERSION_1_MAGIC_NUMBER == magic_v1) return source.readUnsignedByte(); - final int magic_v2 = (magic_v1 << 16) + source.readUnsignedShort(); - if (VERSION_2_MAGIC_NUMBER == magic_v2) return source.readUnsignedShort(); + private static int getFormatVersion(final ByteBuffer buffer) throws IOException { + final int magic_v1 = readUnsignedShort(buffer); + if (VERSION_1_MAGIC_NUMBER == magic_v1) return readUnsignedByte(buffer); + final int magic_v2 = (magic_v1 << 16) + readUnsignedShort(buffer); + if (VERSION_2_MAGIC_NUMBER == magic_v2) return readUnsignedShort(buffer); return NOT_A_VERSION_NUMBER; } @@ -1333,53 +1337,60 @@ public class BinaryDictInputOutput { * The file is read at the current file pointer, so the caller must take care the pointer * is in the right place before calling this. */ - public static void populateOptionsFromFile(final RandomAccessFile source, final long headerSize, - final HashMap options) throws IOException { - while (source.getFilePointer() < headerSize) { - final String key = CharEncoding.readString(source); - final String value = CharEncoding.readString(source); + public static void populateOptions(final ByteBuffer buffer, final int headerSize, + final HashMap options) { + while (buffer.position() < headerSize) { + final String key = CharEncoding.readString(buffer); + final String value = CharEncoding.readString(buffer); options.put(key, value); } } /** - * Reads a random access file and returns the memory representation of the dictionary. + * Reads a byte buffer and returns the memory representation of the dictionary. * * This high-level method takes a binary file and reads its contents, populating a * FusionDictionary structure. The optional dict argument is an existing dictionary to * which words from the file should be added. If it is null, a new dictionary is created. * - * @param source the file to read. + * @param buffer the buffer to read. * @param dict an optional dictionary to add words to, or null. * @return the created (or merged) dictionary. */ - public static FusionDictionary readDictionaryBinary(final RandomAccessFile source, + public static FusionDictionary readDictionaryBinary(final ByteBuffer buffer, final FusionDictionary dict) throws IOException, UnsupportedFormatException { // Check file version - final int version = getFormatVersion(source); - if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION ) { + final int version = getFormatVersion(buffer); + if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION) { throw new UnsupportedFormatException("This file has version " + version + ", but this implementation does not support versions above " + MAXIMUM_SUPPORTED_VERSION); } - // Read options - final int optionsFlags = source.readUnsignedShort(); + // clear cache + wordCache.clear(); - final long headerSize; + // Read options + final int optionsFlags = readUnsignedShort(buffer); + + final int headerSize; final HashMap options = new HashMap(); if (version < FIRST_VERSION_WITH_HEADER_SIZE) { - headerSize = source.getFilePointer(); + headerSize = buffer.position(); } else { - headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16) - + (source.readUnsignedByte() << 8) + source.readUnsignedByte(); - populateOptionsFromFile(source, headerSize, options); - source.seek(headerSize); + headerSize = buffer.getInt(); + populateOptions(buffer, headerSize, options); + buffer.position(headerSize); + } + + if (headerSize < 0) { + throw new UnsupportedFormatException("header size can't be negative."); } Map reverseNodeMapping = new TreeMap(); Map reverseGroupMapping = new TreeMap(); - final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping); + final Node root = readNode( + buffer, headerSize, reverseNodeMapping, reverseGroupMapping); FusionDictionary newDict = new FusionDictionary(root, new FusionDictionary.DictionaryOptions(options, @@ -1402,6 +1413,28 @@ public class BinaryDictInputOutput { return newDict; } + /** + * Helper function to read one byte from ByteBuffer. + */ + private static int readUnsignedByte(final ByteBuffer buffer) { + return ((int)buffer.get()) & 0xFF; + } + + /** + * Helper function to read two byte from ByteBuffer. + */ + private static int readUnsignedShort(final ByteBuffer buffer) { + return ((int)buffer.getShort()) & 0xFFFF; + } + + /** + * Helper function to read three byte from ByteBuffer. + */ + private static int readUnsignedInt24(final ByteBuffer buffer) { + final int value = readUnsignedByte(buffer) << 16; + return value + readUnsignedShort(buffer); + } + /** * Basic test to find out whether the file is a binary dictionary or not. * @@ -1411,14 +1444,26 @@ public class BinaryDictInputOutput { * @return true if it's a binary dictionary, false otherwise */ public static boolean isBinaryDictionary(final String filename) { + FileInputStream inStream = null; try { - RandomAccessFile f = new RandomAccessFile(filename, "r"); - final int version = getFormatVersion(f); + final File file = new File(filename); + inStream = new FileInputStream(file); + final ByteBuffer buffer = inStream.getChannel().map( + FileChannel.MapMode.READ_ONLY, 0, file.length()); + final int version = getFormatVersion(buffer); return (version >= MINIMUM_SUPPORTED_VERSION && version <= MAXIMUM_SUPPORTED_VERSION); } catch (FileNotFoundException e) { return false; } catch (IOException e) { return false; + } finally { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + // do nothing + } + } } } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java new file mode 100644 index 000000000..0094db8a7 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/BinaryDictIOTests.java @@ -0,0 +1,224 @@ +/* + * 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.makedict.BinaryDictInputOutput; +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; +import com.android.inputmethod.latin.makedict.FusionDictionary.Node; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; + +import android.test.AndroidTestCase; +import android.util.Log; +import android.util.SparseArray; + +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.Set; + +/** + * Unit tests for BinaryDictInputOutput + */ +public class BinaryDictIOTests extends AndroidTestCase { + private static final String TAG = BinaryDictIOTests.class.getSimpleName(); + private static final int MAX_UNIGRAMS = 1000; + private static final int UNIGRAM_FREQ = 10; + private static final int BIGRAM_FREQ = 50; + + 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" + }; + + /** + * Generates a random word. + */ + private String generateWord(final int value) { + final int lengthOfChars = CHARACTERS.length; + StringBuilder builder = new StringBuilder("a"); + 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 addUnigrams(final int number, + final FusionDictionary dict, + final List words) { + for (int i = 0; i < number; ++i) { + final String word = words.get(i); + dict.add(word, UNIGRAM_FREQ, null); + } + } + + private void addBigrams(final FusionDictionary dict, + final List words, + final SparseArray> sparseArray) { + for (int i = 0; i < sparseArray.size(); ++i) { + final int w1 = sparseArray.keyAt(i); + for (int w2 : sparseArray.valueAt(i)) { + dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ); + } + } + } + + private long timeWritingDictToFile(final String fileName, + final FusionDictionary dict) { + + final File file = new File(getContext().getFilesDir(), fileName); + long now = -1, diff = -1; + + try { + final FileOutputStream out = new FileOutputStream(file); + + now = System.currentTimeMillis(); + BinaryDictInputOutput.writeDictionaryBinary(out, dict, 2); + diff = System.currentTimeMillis() - now; + + out.flush(); + out.close(); + } catch (IOException e) { + Log.e(TAG, "IO exception while writing file: " + e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "UnsupportedFormatException: " + e); + } + + return diff; + } + + private void checkDictionary(final FusionDictionary dict, + final List words, + final SparseArray> bigrams) { + assertNotNull(dict); + + // check unigram + for (final String word : words) { + final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, word); + assertNotNull(cg); + } + + // check bigram + for (int i = 0; i < bigrams.size(); ++i) { + final int w1 = bigrams.keyAt(i); + for (final int w2 : bigrams.valueAt(i)) { + final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, words.get(w1)); + assertNotNull(words.get(w1) + "," + words.get(w2), cg.getBigram(words.get(w2))); + } + } + } + + private long timeReadingAndCheckDict(final String fileName, + final List words, + final SparseArray> bigrams) { + + long now, diff = -1; + + try { + final File file = new File(getContext().getFilesDir(), fileName); + final FileInputStream inStream = new FileInputStream(file); + final ByteBuffer buffer = inStream.getChannel().map( + FileChannel.MapMode.READ_ONLY, 0, file.length()); + + now = System.currentTimeMillis(); + + final FusionDictionary dict = + BinaryDictInputOutput.readDictionaryBinary(buffer, null); + + diff = System.currentTimeMillis() - now; + + checkDictionary(dict, words, bigrams); + return diff; + + } catch (IOException e) { + Log.e(TAG, "raise IOException while reading file " + e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Unsupported format: " + e); + } + + return diff; + } + + private String runReadAndWrite(final List words, + final SparseArray> bigrams, + final String message) { + final FusionDictionary dict = new FusionDictionary(new Node(), + new FusionDictionary.DictionaryOptions( + new HashMap(), false, false)); + + final String fileName = generateWord((int)System.currentTimeMillis()) + ".dict"; + + addUnigrams(words.size(), dict, words); + addBigrams(dict, words, bigrams); + // check original dictionary + checkDictionary(dict, words, bigrams); + + final long write = timeWritingDictToFile(fileName, dict); + final long read = timeReadingAndCheckDict(fileName, words, bigrams); + deleteFile(fileName); + + return "PROF: read=" + read + "ms, write=" + write + "ms :" + message; + } + + private void deleteFile(final String fileName) { + final File file = new File(getContext().getFilesDir(), fileName); + file.delete(); + } + + public void testReadAndWrite() { + final List results = new ArrayList(); + + final Random random = new Random(123456); + final List words = generateWords(MAX_UNIGRAMS, random); + final SparseArray> emptyArray = CollectionUtils.newSparseArray(); + + final SparseArray> chain = CollectionUtils.newSparseArray(); + for (int i = 0; i < words.size(); ++i) chain.put(i, new ArrayList()); + for (int i = 1; i < words.size(); ++i) chain.get(i-1).add(i); + + final SparseArray> star = CollectionUtils.newSparseArray(); + final List list0 = CollectionUtils.newArrayList(); + star.put(0, list0); + for (int i = 1; i < words.size(); ++i) star.get(0).add(i); + + results.add(runReadAndWrite(words, emptyArray, "only unigram")); + results.add(runReadAndWrite(words, chain, "chain")); + results.add(runReadAndWrite(words, star, "star")); + + for (final String result : results) { + Log.d(TAG, result); + } + } +} diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java index 25e1740cb..fbfc1dabb 100644 --- a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -27,7 +27,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; -import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.LinkedList; @@ -238,8 +239,23 @@ public class DictionaryMaker { */ private static FusionDictionary readBinaryFile(final String binaryFilename) throws FileNotFoundException, IOException, UnsupportedFormatException { - final RandomAccessFile inputFile = new RandomAccessFile(binaryFilename, "r"); - return BinaryDictInputOutput.readDictionaryBinary(inputFile, null); + FileInputStream inStream = null; + + try { + final File file = new File(binaryFilename); + inStream = new FileInputStream(file); + final ByteBuffer buffer = inStream.getChannel().map( + FileChannel.MapMode.READ_ONLY, 0, file.length()); + return BinaryDictInputOutput.readDictionaryBinary(buffer, null); + } finally { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + // do nothing + } + } + } } /** From 233aad5e5c7567a97af30f38f50a65365f729dfe Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 24 Aug 2012 14:45:54 +0900 Subject: [PATCH 117/133] Refactor Change-Id: I907126034d9cc821df8353a78f713638dfc322d6 --- native/jni/src/proximity_info_state.cpp | 8 ++++---- native/jni/src/proximity_info_state.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index 0bb6dd9cb..422068db2 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -25,7 +25,7 @@ #include "proximity_info_state.h" namespace latinime { -void ProximityInfoState::initInputParams(const int pointerId, const float maxLength, +void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, const int *const xCoordinates, const int *const yCoordinates, const int *const times, const int *const pointerIds, const bool isGeometric) { @@ -68,7 +68,7 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxLen } } - mMaxPointToKeyLength = maxLength; + mMaxPointToKeyLength = maxPointToKeyLength; /////////////////////// // Setup touch points mInputXs.clear(); @@ -115,8 +115,8 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxLen } mPrimaryInputWord[inputSize] = 0; - mTouchPositionCorrectionEnabled = - mHasTouchPositionCorrectionData && xCoordinates && yCoordinates && !isGeometric; + mTouchPositionCorrectionEnabled = mInputSize > 0 && mHasTouchPositionCorrectionData + && xCoordinates && yCoordinates && !isGeometric; for (int i = 0; i < mInputSize && mTouchPositionCorrectionEnabled; ++i) { const int *proximityChars = getProximityCharsAt(i); const int primaryKey = proximityChars[0]; diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 81d68fdcb..178000446 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -41,7 +41,7 @@ class ProximityInfoState { ///////////////////////////////////////// // Defined in proximity_info_state.cpp // ///////////////////////////////////////// - void initInputParams(const int pointerId, const float maxLength, + void initInputParams(const int pointerId, const float maxPointToKeyLength, const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, const int *xCoordinates, const int *yCoordinates, const int *const times, const int *const pointerIds, const bool isGeometric); From 333a300586c3bedb3d51524642b542cefaa1a22d Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 20 Aug 2012 12:57:34 +0900 Subject: [PATCH 118/133] Add multiple gesture preview trails animation Change-Id: I533e6de9b138317472565be82c8ba5e422472888 --- java/res/values/attrs.xml | 6 + java/res/values/config.xml | 3 + java/res/values/styles.xml | 3 + .../inputmethod/keyboard/PointerTracker.java | 26 ++- .../internal/GesturePreviewTrail.java | 161 ++++++++++++++++++ .../keyboard/internal/GestureStroke.java | 29 +--- .../GestureStrokeWithPreviewTrail.java | 70 ++++++++ .../keyboard/internal/PreviewPlacerView.java | 79 ++++++--- 8 files changed, 316 insertions(+), 61 deletions(-) create mode 100644 java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java create mode 100644 java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 4975d6540..cced45d9f 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -131,6 +131,12 @@ + + + + + + diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 54a6687a3..8477df054 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -50,6 +50,9 @@ --> 70 200 + 100 + 1000 + 20 diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index ae67c4369..e5e7fed80 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -78,6 +78,9 @@ @android:color/white @dimen/gesture_floating_preview_text_connector_width @integer/config_gesture_floating_preview_text_linger_timeout + @integer/config_gesture_preview_trail_fadeout_start_delay + @integer/config_gesture_preview_trail_fadeout_duration + @integer/config_gesture_preview_trail_update_interval @android:color/holo_blue_light @dimen/gesture_preview_trail_width diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 67f37a7e7..b5b3ef65f 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -17,14 +17,13 @@ package com.android.inputmethod.keyboard; import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.internal.GestureStroke; +import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewTrail; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.InputPointers; @@ -211,7 +210,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { private static final KeyboardActionListener EMPTY_LISTENER = new KeyboardActionListener.Adapter(); - private final GestureStroke mGestureStroke; + private final GestureStrokeWithPreviewTrail mGestureStrokeWithPreviewTrail; public static void init(boolean hasDistinctMultitouch, boolean needsPhantomSuddenMoveEventHack) { @@ -297,7 +296,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.mGestureStroke.appendIncrementalBatchPoints(sAggregratedPointers); + tracker.mGestureStrokeWithPreviewTrail.appendIncrementalBatchPoints( + sAggregratedPointers); } return sAggregratedPointers; } @@ -308,7 +308,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.mGestureStroke.appendAllBatchPoints(sAggregratedPointers); + tracker.mGestureStrokeWithPreviewTrail.appendAllBatchPoints(sAggregratedPointers); } return sAggregratedPointers; } @@ -319,7 +319,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.mGestureStroke.reset(); + tracker.mGestureStrokeWithPreviewTrail.reset(); } sAggregratedPointers.reset(); } @@ -329,7 +329,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { throw new NullPointerException(); } mPointerId = id; - mGestureStroke = new GestureStroke(id); + mGestureStrokeWithPreviewTrail = new GestureStrokeWithPreviewTrail(id); setKeyDetectorInner(handler.getKeyDetector()); mListener = handler.getKeyboardActionListener(); mDrawingProxy = handler.getDrawingProxy(); @@ -429,7 +429,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard(); - mGestureStroke.setGestureSampleLength(mKeyboard.mMostCommonKeyWidth); + mGestureStrokeWithPreviewTrail.setGestureSampleLength(mKeyboard.mMostCommonKeyWidth); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); if (newKey != mCurrentKey) { if (mDrawingProxy != null) { @@ -539,10 +539,8 @@ public class PointerTracker implements PointerTrackerQueue.Element { mDrawingProxy.invalidateKey(key); } - public void drawGestureTrail(final Canvas canvas, final Paint paint) { - if (mInGesture) { - mGestureStroke.drawGestureTrail(canvas, paint); - } + public GestureStrokeWithPreviewTrail getGestureStrokeWithPreviewTrail() { + return mGestureStrokeWithPreviewTrail; } public int getLastX() { @@ -692,7 +690,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { mIsPossibleGesture = true; // TODO: pointer times should be relative to first down even in entire batch input // instead of resetting to 0 for each new down event. - mGestureStroke.addPoint(x, y, 0, false); + mGestureStrokeWithPreviewTrail.addPoint(x, y, 0, false); } } } @@ -733,7 +731,7 @@ public class PointerTracker implements PointerTrackerQueue.Element { final long eventTime, final boolean isHistorical, final Key key) { final int gestureTime = (int)(eventTime - tracker.getDownTime()); if (sShouldHandleGesture && mIsPossibleGesture) { - final GestureStroke stroke = mGestureStroke; + final GestureStroke stroke = mGestureStrokeWithPreviewTrail; stroke.addPoint(x, y, gestureTime, isHistorical); if (!mInGesture && stroke.isStartOfAGesture()) { startBatchInput(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java new file mode 100644 index 000000000..edc20998f --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java @@ -0,0 +1,161 @@ +/* + * 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.keyboard.internal; + +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.SystemClock; + +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.ResizableIntArray; + +class GesturePreviewTrail { + private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewTrail.PREVIEW_CAPACITY; + + private final GesturePreviewTrailParams mPreviewParams; + private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); + private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); + private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY); + private int mCurrentStrokeId; + private long mCurrentDownTime; + + // Use this value as imaginary zero because x-coordinates may be zero. + private static final int DOWN_EVENT_MARKER = -128; + + static class GesturePreviewTrailParams { + public final int mFadeoutStartDelay; + public final int mFadeoutDuration; + public final int mUpdateInterval; + + public GesturePreviewTrailParams(final TypedArray keyboardViewAttr) { + mFadeoutStartDelay = keyboardViewAttr.getInt( + R.styleable.KeyboardView_gesturePreviewTrailFadeoutStartDelay, 0); + mFadeoutDuration = keyboardViewAttr.getInt( + R.styleable.KeyboardView_gesturePreviewTrailFadeoutDuration, 0); + mUpdateInterval = keyboardViewAttr.getInt( + R.styleable.KeyboardView_gesturePreviewTrailUpdateInterval, 0); + } + } + + public GesturePreviewTrail(final GesturePreviewTrailParams params) { + mPreviewParams = params; + } + + private static int markAsDownEvent(final int xCoord) { + return DOWN_EVENT_MARKER - xCoord; + } + + private static boolean isDownEventXCoord(final int xCoordOrMark) { + return xCoordOrMark <= DOWN_EVENT_MARKER; + } + + private static int getXCoordValue(final int xCoordOrMark) { + return isDownEventXCoord(xCoordOrMark) + ? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark; + } + + public void addStroke(final GestureStrokeWithPreviewTrail stroke, final long downTime) { + final int strokeId = stroke.getGestureStrokeId(); + final boolean isNewStroke = strokeId != mCurrentStrokeId; + final int trailSize = mEventTimes.getLength(); + stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates); + final int newTrailSize = mEventTimes.getLength(); + if (stroke.getGestureStrokePreviewSize() == 0) { + return; + } + if (isNewStroke) { + final int elapsedTime = (int)(downTime - mCurrentDownTime); + final int[] eventTimes = mEventTimes.getPrimitiveArray(); + for (int i = 0; i < trailSize; i++) { + eventTimes[i] -= elapsedTime; + } + + if (newTrailSize > trailSize) { + final int[] xCoords = mXCoordinates.getPrimitiveArray(); + xCoords[trailSize] = markAsDownEvent(xCoords[trailSize]); + } + mCurrentDownTime = downTime; + mCurrentStrokeId = strokeId; + } + } + + private int getAlpha(final int elapsedTime) { + if (elapsedTime < mPreviewParams.mFadeoutStartDelay) { + return Constants.Color.ALPHA_OPAQUE; + } + final int decreasingAlpha = Constants.Color.ALPHA_OPAQUE + * (elapsedTime - mPreviewParams.mFadeoutStartDelay) + / mPreviewParams.mFadeoutDuration; + return Constants.Color.ALPHA_OPAQUE - decreasingAlpha; + } + + /** + * Draw gesture preview trail + * @param canvas The canvas to draw the gesture preview trail + * @param paint The paint object to be used to draw the gesture preview trail + * @return true if some gesture preview trails remain to be drawn + */ + public boolean drawGestureTrail(final Canvas canvas, final Paint paint) { + final int trailSize = mEventTimes.getLength(); + if (trailSize == 0) { + return false; + } + + final int[] eventTimes = mEventTimes.getPrimitiveArray(); + final int[] xCoords = mXCoordinates.getPrimitiveArray(); + final int[] yCoords = mYCoordinates.getPrimitiveArray(); + final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentDownTime); + final int lingeringDuration = mPreviewParams.mFadeoutStartDelay + + mPreviewParams.mFadeoutDuration; + int startIndex; + for (startIndex = 0; startIndex < trailSize; startIndex++) { + final int elapsedTime = sinceDown - eventTimes[startIndex]; + // Skip too old trail points. + if (elapsedTime < lingeringDuration) { + break; + } + } + + if (startIndex < trailSize) { + int lastX = getXCoordValue(xCoords[startIndex]); + int lastY = yCoords[startIndex]; + for (int i = startIndex + 1; i < trailSize - 1; i++) { + final int x = xCoords[i]; + final int y = yCoords[i]; + final int elapsedTime = sinceDown - eventTimes[i]; + // Draw trail line only when the current point isn't a down point. + if (!isDownEventXCoord(x)) { + paint.setAlpha(getAlpha(elapsedTime)); + canvas.drawLine(lastX, lastY, x, y, paint); + } + lastX = getXCoordValue(x); + lastY = y; + } + } + + // TODO: Implement ring buffer to avoid moving points. + // Discard faded out points. + final int newSize = trailSize - startIndex; + System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize); + System.arraycopy(xCoords, startIndex, xCoords, 0, newSize); + System.arraycopy(yCoords, startIndex, yCoords, 0, newSize); + mEventTimes.setLength(newSize); + mXCoordinates.setLength(newSize); + mYCoordinates.setLength(newSize); + return newSize > 0; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java index 79e977a40..292842d22 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java @@ -14,10 +14,6 @@ package com.android.inputmethod.keyboard.internal; -import android.graphics.Canvas; -import android.graphics.Paint; - -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.ResizableIntArray; @@ -48,13 +44,8 @@ public class GestureStroke { private static final float DOUBLE_PI = (float)(2.0f * Math.PI); - // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT - private static final int DRAWING_GESTURE_FADE_START = 10; - private static final int DRAWING_GESTURE_FADE_RATE = 6; - - public GestureStroke(int pointerId) { + public GestureStroke(final int pointerId) { mPointerId = pointerId; - reset(); } public void setGestureSampleLength(final int keyWidth) { @@ -158,7 +149,7 @@ public class GestureStroke { if (dx == 0 && dy == 0) return 0; // Would it be faster to call atan2f() directly via JNI? Not sure about what the JIT // does with Math.atan2(). - return (float)Math.atan2((double)dy, (double)dx); + return (float)Math.atan2(dy, dx); } private static float getAngleDiff(final float a1, final float a2) { @@ -168,20 +159,4 @@ public class GestureStroke { } return diff; } - - public void drawGestureTrail(final Canvas canvas, final Paint paint) { - // TODO: These paint parameter interpolation should be tunable, possibly introduce an object - // that implements an interface such as Paint getPaint(int step, int strokePoints) - final int size = mXCoordinates.getLength(); - final int[] xCoords = mXCoordinates.getPrimitiveArray(); - final int[] yCoords = mYCoordinates.getPrimitiveArray(); - int alpha = Constants.Color.ALPHA_OPAQUE; - for (int i = size - 1; i > 0 && alpha > 0; i--) { - paint.setAlpha(alpha); - if (size - i > DRAWING_GESTURE_FADE_START) { - alpha -= DRAWING_GESTURE_FADE_RATE; - } - canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint); - } - } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java new file mode 100644 index 000000000..6c1a9bc01 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java @@ -0,0 +1,70 @@ +/* + * 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.keyboard.internal; + +import com.android.inputmethod.latin.ResizableIntArray; + +public class GestureStrokeWithPreviewTrail extends GestureStroke { + public static final int PREVIEW_CAPACITY = 256; + + private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY); + private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY); + private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY); + + private int mStrokeId; + private int mLastPreviewSize; + + public GestureStrokeWithPreviewTrail(final int pointerId) { + super(pointerId); + } + + @Override + public void reset() { + super.reset(); + mStrokeId++; + mLastPreviewSize = 0; + mPreviewEventTimes.setLength(0); + mPreviewXCoordinates.setLength(0); + mPreviewYCoordinates.setLength(0); + } + + public int getGestureStrokeId() { + return mStrokeId; + } + + public int getGestureStrokePreviewSize() { + return mPreviewEventTimes.getLength(); + } + + @Override + public void addPoint(final int x, final int y, final int time, final boolean isHistorical) { + super.addPoint(x, y, time, isHistorical); + mPreviewEventTimes.add(time); + mPreviewXCoordinates.add(x); + mPreviewYCoordinates.add(y); + } + + public void appendPreviewStroke(final ResizableIntArray eventTimes, + final ResizableIntArray xCoords, final ResizableIntArray yCoords) { + final int length = mPreviewEventTimes.getLength() - mLastPreviewSize; + if (length <= 0) { + return; + } + eventTimes.append(mPreviewEventTimes, mLastPreviewSize, length); + xCoords.append(mPreviewXCoordinates, mLastPreviewSize, length); + yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length); + mLastPreviewSize = mPreviewEventTimes.getLength(); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java index 3f33aee5a..269b202b5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java @@ -28,6 +28,7 @@ import android.util.SparseArray; import android.widget.RelativeLayout; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.GesturePreviewTrailParams; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; @@ -48,7 +49,9 @@ public class PreviewPlacerView extends RelativeLayout { private int mXOrigin; private int mYOrigin; - private final SparseArray mPointers = CollectionUtils.newSparseArray(); + private final SparseArray mGesturePreviewTrails = + CollectionUtils.newSparseArray(); + private final GesturePreviewTrailParams mGesturePreviewTrailParams; private String mGestureFloatingPreviewText; private int mLastPointerX; @@ -57,23 +60,31 @@ public class PreviewPlacerView extends RelativeLayout { private boolean mDrawsGesturePreviewTrail; private boolean mDrawsGestureFloatingPreviewText; - private final DrawingHandler mDrawingHandler = new DrawingHandler(this); + private final DrawingHandler mDrawingHandler; private static class DrawingHandler extends StaticInnerHandlerWrapper { private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 0; + private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 1; - public DrawingHandler(PreviewPlacerView outerInstance) { + private final GesturePreviewTrailParams mGesturePreviewTrailParams; + + public DrawingHandler(final PreviewPlacerView outerInstance, + final GesturePreviewTrailParams gesturePreviewTrailParams) { super(outerInstance); + mGesturePreviewTrailParams = gesturePreviewTrailParams; } @Override - public void handleMessage(Message msg) { + public void handleMessage(final Message msg) { final PreviewPlacerView placerView = getOuterInstance(); if (placerView == null) return; switch (msg.what) { case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: placerView.setGestureFloatingPreviewText(null); break; + case MSG_UPDATE_GESTURE_PREVIEW_TRAIL: + placerView.invalidate(); + break; } } @@ -89,16 +100,27 @@ public class PreviewPlacerView extends RelativeLayout { placerView.mGestureFloatingPreviewTextLingerTimeout); } + private void cancelUpdateGestureTrailPreview() { + removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL); + } + + public void postUpdateGestureTrailPreview() { + cancelUpdateGestureTrailPreview(); + sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL), + mGesturePreviewTrailParams.mUpdateInterval); + } + public void cancelAllMessages() { cancelDismissGestureFloatingPreviewText(); + cancelUpdateGestureTrailPreview(); } } - public PreviewPlacerView(Context context, AttributeSet attrs) { + public PreviewPlacerView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.keyboardViewStyle); } - public PreviewPlacerView(Context context, AttributeSet attrs, int defStyle) { + public PreviewPlacerView(final Context context, final AttributeSet attrs, final int defStyle) { super(context); setWillNotDraw(false); @@ -128,8 +150,11 @@ public class PreviewPlacerView extends RelativeLayout { R.styleable.KeyboardView_gesturePreviewTrailColor, 0); final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize( R.styleable.KeyboardView_gesturePreviewTrailWidth, 0); + mGesturePreviewTrailParams = new GesturePreviewTrailParams(keyboardViewAttr); keyboardViewAttr.recycle(); + mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams); + mGesturePaint = new Paint(); mGesturePaint.setAntiAlias(true); mGesturePaint.setStyle(Paint.Style.STROKE); @@ -144,21 +169,28 @@ public class PreviewPlacerView extends RelativeLayout { mTextPaint.setTextSize(gestureFloatingPreviewTextSize); } - public void setOrigin(int x, int y) { + public void setOrigin(final int x, final int y) { mXOrigin = x; mYOrigin = y; } - public void setGesturePreviewMode(boolean drawsGesturePreviewTrail, - boolean drawsGestureFloatingPreviewText) { + public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail, + final boolean drawsGestureFloatingPreviewText) { mDrawsGesturePreviewTrail = drawsGesturePreviewTrail; mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText; } - public void invalidatePointer(PointerTracker tracker) { - synchronized (mPointers) { - mPointers.put(tracker.mPointerId, tracker); + public void invalidatePointer(final PointerTracker tracker) { + GesturePreviewTrail trail; + synchronized (mGesturePreviewTrails) { + trail = mGesturePreviewTrails.get(tracker.mPointerId); + if (trail == null) { + trail = new GesturePreviewTrail(mGesturePreviewTrailParams); + mGesturePreviewTrails.put(tracker.mPointerId, trail); + } } + trail.addStroke(tracker.getGestureStrokeWithPreviewTrail(), tracker.getDownTime()); + mLastPointerX = tracker.getLastX(); mLastPointerY = tracker.getLastY(); // TODO: Should narrow the invalidate region. @@ -166,17 +198,23 @@ public class PreviewPlacerView extends RelativeLayout { } @Override - public void onDraw(Canvas canvas) { + public void onDraw(final Canvas canvas) { super.onDraw(canvas); canvas.translate(mXOrigin, mYOrigin); if (mDrawsGesturePreviewTrail) { - synchronized (mPointers) { - final int trackerCount = mPointers.size(); - for (int index = 0; index < trackerCount; index++) { - final PointerTracker tracker = mPointers.valueAt(index); - tracker.drawGestureTrail(canvas, mGesturePaint); + 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(canvas, mGesturePaint); } } + if (needsUpdatingGesturePreviewTrail) { + mDrawingHandler.postUpdateGestureTrailPreview(); + } } if (mDrawsGestureFloatingPreviewText) { drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText); @@ -184,7 +222,7 @@ public class PreviewPlacerView extends RelativeLayout { canvas.translate(-mXOrigin, -mYOrigin); } - public void setGestureFloatingPreviewText(String gestureFloatingPreviewText) { + public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) { mGestureFloatingPreviewText = gestureFloatingPreviewText; invalidate(); } @@ -197,7 +235,8 @@ public class PreviewPlacerView extends RelativeLayout { mDrawingHandler.cancelAllMessages(); } - private void drawGestureFloatingPreviewText(Canvas canvas, String gestureFloatingPreviewText) { + private void drawGestureFloatingPreviewText(final Canvas canvas, + final String gestureFloatingPreviewText) { if (TextUtils.isEmpty(gestureFloatingPreviewText)) { return; } From 5e3b93542d2096b0537390996c04a23013e325a5 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 24 Aug 2012 17:48:23 +0900 Subject: [PATCH 119/133] Fix the bug that the first gesture preview trail isn't drawn Change-Id: Ib3acd7c0b401e6d95b39552b48d0d974793eb438 --- .../inputmethod/keyboard/internal/GesturePreviewTrail.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java index edc20998f..747627b7d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java @@ -30,7 +30,7 @@ class GesturePreviewTrail { private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY); private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY); - private int mCurrentStrokeId; + private int mCurrentStrokeId = -1; private long mCurrentDownTime; // Use this value as imaginary zero because x-coordinates may be zero. From 08f00cf55f2e083c1ed254a32495b622c9ad9862 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Fri, 24 Aug 2012 15:19:56 +0900 Subject: [PATCH 120/133] Step 39-A Cleanup proximity_info_state Change-Id: I6070929f310f7dc6359d12d62e81d2e2e032c38e --- native/jni/src/proximity_info_state.cpp | 114 ++++++++++++------------ native/jni/src/proximity_info_state.h | 6 +- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp index 422068db2..897ad46c4 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/proximity_info_state.cpp @@ -26,7 +26,7 @@ namespace latinime { void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, - const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, + const ProximityInfo *proximityInfo, const int32_t *const inputCodes, const int inputSize, const int *const xCoordinates, const int *const yCoordinates, const int *const times, const int *const pointerIds, const bool isGeometric) { mProximityInfo = proximityInfo; @@ -38,39 +38,39 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi mCellWidth = proximityInfo->getCellWidth(); mGridHeight = proximityInfo->getGridWidth(); mGridWidth = proximityInfo->getGridHeight(); - const int normalizedSquaredDistancesLength = - MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL; - for (int i = 0; i < normalizedSquaredDistancesLength; ++i) { - mNormalizedSquaredDistances[i] = NOT_A_DISTANCE; - } - memset(mInputCodes, 0, - MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE_INTERNAL * sizeof(mInputCodes[0])); + memset(mInputCodes, 0, sizeof(mInputCodes)); - for (int i = 0; i < inputSize; ++i) { - const int32_t primaryKey = inputCodes[i]; - const int x = xCoordinates[i]; - const int y = yCoordinates[i]; - int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL]; - mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities); - } - - if (DEBUG_PROXIMITY_CHARS) { + if (!isGeometric && pointerId == 0) { + // Initialize + // - mInputCodes + // - mNormalizedSquaredDistances + // TODO: Merge for (int i = 0; i < inputSize; ++i) { - AKLOGI("---"); - for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) { - int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; - int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; - icc += 0; - icfjc += 0; - AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc); + const int32_t primaryKey = inputCodes[i]; + const int x = xCoordinates[i]; + const int y = yCoordinates[i]; + int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL]; + mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities); + } + + if (DEBUG_PROXIMITY_CHARS) { + for (int i = 0; i < inputSize; ++i) { + AKLOGI("---"); + for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) { + int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; + int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j]; + icc += 0; + icfjc += 0; + AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc); + } } } } - mMaxPointToKeyLength = maxPointToKeyLength; /////////////////////// // Setup touch points + mMaxPointToKeyLength = maxPointToKeyLength; mInputXs.clear(); mInputYs.clear(); mTimes.clear(); @@ -81,10 +81,10 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi if (xCoordinates && yCoordinates) { const bool proximityOnly = !isGeometric && (xCoordinates[0] < 0 || yCoordinates[0] < 0); for (int i = 0; i < inputSize; ++i) { - ++mInputSize; // Assuming pointerId == 0 if pointerIds is null. const int pid = pointerIds ? pointerIds[i] : 0; if (pointerId == pid) { + ++mInputSize; const int c = isGeometric ? NOT_A_COORDINATE : getPrimaryCharAt(i); const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i]; const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i]; @@ -107,42 +107,46 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi } } } + // end /////////////////////// - for (int i = 0; i < inputSize; ++i) { - mPrimaryInputWord[i] = getPrimaryCharAt(i); - } - mPrimaryInputWord[inputSize] = 0; - + memset(mNormalizedSquaredDistances, NOT_A_DISTANCE, sizeof(mNormalizedSquaredDistances)); + memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord)); mTouchPositionCorrectionEnabled = mInputSize > 0 && mHasTouchPositionCorrectionData && xCoordinates && yCoordinates && !isGeometric; - for (int i = 0; i < mInputSize && mTouchPositionCorrectionEnabled; ++i) { - const int *proximityChars = getProximityCharsAt(i); - const int primaryKey = proximityChars[0]; - const int x = xCoordinates[i]; - const int y = yCoordinates[i]; - if (DEBUG_PROXIMITY_CHARS) { - int a = x + y + primaryKey; - a += 0; - AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y); + if (!isGeometric && pointerId == 0) { + for (int i = 0; i < inputSize; ++i) { + mPrimaryInputWord[i] = getPrimaryCharAt(i); } - for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityChars[j] > 0; ++j) { - const int currentChar = proximityChars[j]; - const float squaredDistance = - hasInputCoordinates() ? calculateNormalizedSquaredDistance( - mProximityInfo->getKeyIndex(currentChar), i) : - NOT_A_DISTANCE_FLOAT; - if (squaredDistance >= 0.0f) { - mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = - (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR); - } else { - mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = - (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO : - PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO; - } + + for (int i = 0; i < mInputSize && mTouchPositionCorrectionEnabled; ++i) { + const int *proximityChars = getProximityCharsAt(i); + const int primaryKey = proximityChars[0]; + const int x = xCoordinates[i]; + const int y = yCoordinates[i]; if (DEBUG_PROXIMITY_CHARS) { - AKLOGI("--- Proximity (%d) = %c", j, currentChar); + int a = x + y + primaryKey; + a += 0; + AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y); + } + for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityChars[j] > 0; ++j) { + const int currentChar = proximityChars[j]; + const float squaredDistance = + hasInputCoordinates() ? calculateNormalizedSquaredDistance( + mProximityInfo->getKeyIndex(currentChar), i) : + NOT_A_DISTANCE_FLOAT; + if (squaredDistance >= 0.0f) { + mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = + (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR); + } else { + mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] = + (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO : + PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO; + } + if (DEBUG_PROXIMITY_CHARS) { + AKLOGI("--- Proximity (%d) = %c", j, currentChar); + } } } } diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 178000446..687a552c1 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -42,9 +42,9 @@ class ProximityInfoState { // Defined in proximity_info_state.cpp // ///////////////////////////////////////// void initInputParams(const int pointerId, const float maxPointToKeyLength, - const ProximityInfo *proximityInfo, const int32_t *inputCodes, const int inputSize, - const int *xCoordinates, const int *yCoordinates, const int *const times, - const int *const pointerIds, const bool isGeometric); + const ProximityInfo *proximityInfo, const int32_t *const inputCodes, + const int inputSize, const int *xCoordinates, const int *yCoordinates, + const int *const times, const int *const pointerIds, const bool isGeometric); ///////////////////////////////////////// // Defined here // From 162c211b44c1546b2e9be36e0cec50de497217a9 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Fri, 24 Aug 2012 14:51:15 +0900 Subject: [PATCH 121/133] Address warnings by -Weffc++ Change-Id: Icd60f1ed2ace0b975d8785bc8a386c7944846075 --- ...oid_inputmethod_latin_BinaryDictionary.cpp | 8 +++---- native/jni/src/correction.cpp | 3 --- native/jni/src/correction.h | 21 +++++++++++++++++-- native/jni/src/dictionary.cpp | 19 ++++++++--------- .../jni/src/gesture/gesture_decoder_wrapper.h | 4 ++-- .../src/gesture/incremental_decoder_wrapper.h | 4 ++-- native/jni/src/proximity_info.cpp | 13 +++++++----- native/jni/src/proximity_info.h | 6 ++---- native/jni/src/proximity_info_state.h | 14 ++++++++++++- native/jni/src/terminal_attributes.h | 21 +++++++++---------- native/jni/src/words_priority_queue.h | 11 +++++----- native/jni/src/words_priority_queue_pool.h | 11 +++++----- 12 files changed, 80 insertions(+), 55 deletions(-) diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 6a0a7c2a0..a20958a88 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -172,10 +172,10 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, int spaceIndices[spaceIndicesLength]; const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); int outputTypes[outputTypesLength]; - memset(outputChars, 0, outputCharsLength * sizeof(outputChars[0])); - memset(scores, 0, scoresLength * sizeof(scores[0])); - memset(spaceIndices, 0, spaceIndicesLength * sizeof(spaceIndices[0])); - memset(outputTypes, 0, outputTypesLength * sizeof(outputTypes[0])); + memset(outputChars, 0, sizeof(outputChars)); + memset(scores, 0, sizeof(scores)); + memset(spaceIndices, 0, sizeof(spaceIndices)); + memset(outputTypes, 0, sizeof(outputTypes)); int count; if (isGesture || arraySize > 0) { diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index 342dec9a3..9ad65b09d 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -627,9 +627,6 @@ Correction::CorrectionType Correction::processCharAndCalcState( } } -Correction::~Correction() { -} - inline static int getQuoteCount(const unsigned short *word, const int length) { int quoteCount = 0; for (int i = 0; i < length; ++i) { diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h index c90c8d553..f016d5453 100644 --- a/native/jni/src/correction.h +++ b/native/jni/src/correction.h @@ -18,6 +18,7 @@ #define LATINIME_CORRECTION_H #include +#include // for memset() #include #include "correction_state.h" @@ -38,8 +39,24 @@ class Correction { NOT_ON_TERMINAL } CorrectionType; - Correction() {}; - virtual ~Correction(); + Correction() + : mProximityInfo(0), mUseFullEditDistance(false), mDoAutoCompletion(false), + mMaxEditDistance(0), mMaxDepth(0), mInputSize(0), mSpaceProximityPos(0), + mMissingSpacePos(0), mTerminalInputIndex(0), mTerminalOutputIndex(0), mMaxErrors(0), + mTotalTraverseCount(0), mNeedsToTraverseAllNodes(false), mOutputIndex(0), + mInputIndex(0), mEquivalentCharCount(0), mProximityCount(0), mExcessiveCount(0), + mTransposedCount(0), mSkippedCount(0), mTransposedPos(0), mExcessivePos(0), + mSkipPos(0), mLastCharExceeded(false), mMatching(false), mProximityMatching(false), + mAdditionalProximityMatching(false), mExceeding(false), mTransposing(false), + mSkipping(false), mProximityInfoState() { + memset(mWord, 0, sizeof(mWord)); + memset(mDistances, 0, sizeof(mDistances)); + memset(mEditDistanceTable, 0, sizeof(mEditDistanceTable)); + // NOTE: mCorrectionStates is an array of instances. + // No need to initialize it explicitly here. + } + + virtual ~Correction() {} void resetCorrection(); void initCorrection( const ProximityInfo *pi, const int inputSize, const int maxWordLength); diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp index 8ce8c8ba6..2fbe83e86 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/dictionary.cpp @@ -30,11 +30,15 @@ namespace latinime { // TODO: Change the type of all keyCodes to uint32_t Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, - int typedLetterMultiplier, int fullWordMultiplier, - int maxWordLength, int maxWords, int maxPredictions) - : mDict(static_cast(dict)), - mOffsetDict((static_cast(dict)) + BinaryFormat::getHeaderSize(mDict)), - mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust) { + int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, + int maxPredictions) + : mDict(static_cast(dict)), + mOffsetDict((static_cast(dict)) + BinaryFormat::getHeaderSize(mDict)), + mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust), + mUnigramDictionary(new UnigramDictionary(mOffsetDict, typedLetterMultiplier, + fullWordMultiplier, maxWordLength, maxWords, BinaryFormat::getFlags(mDict))), + mBigramDictionary(new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions)), + mGestureDecoder(new GestureDecoderWrapper(maxWordLength, maxWords)) { if (DEBUG_DICT) { if (MAX_WORD_LENGTH_INTERNAL < maxWordLength) { AKLOGI("Max word length (%d) is greater than %d", @@ -42,11 +46,6 @@ Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, AKLOGI("IN NATIVE SUGGEST Version: %d", (mDict[0] & 0xFF)); } } - const unsigned int options = BinaryFormat::getFlags(mDict); - mUnigramDictionary = new UnigramDictionary(mOffsetDict, typedLetterMultiplier, - fullWordMultiplier, maxWordLength, maxWords, options); - mBigramDictionary = new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions); - mGestureDecoder = new GestureDecoderWrapper(maxWordLength, maxWords); } Dictionary::~Dictionary() { diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h index 2f6220b92..92e1ded49 100644 --- a/native/jni/src/gesture/gesture_decoder_wrapper.h +++ b/native/jni/src/gesture/gesture_decoder_wrapper.h @@ -29,8 +29,8 @@ class ProximityInfo; class GestureDecoderWrapper : public IncrementalDecoderInterface { public: - GestureDecoderWrapper(const int maxWordLength, const int maxWords) { - mIncrementalDecoderInterface = getGestureDecoderInstance(maxWordLength, maxWords); + GestureDecoderWrapper(const int maxWordLength, const int maxWords) + : mIncrementalDecoderInterface(getGestureDecoderInstance(maxWordLength, maxWords)) { } virtual ~GestureDecoderWrapper() { diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h index 1f4d8a047..da7afdb8a 100644 --- a/native/jni/src/gesture/incremental_decoder_wrapper.h +++ b/native/jni/src/gesture/incremental_decoder_wrapper.h @@ -29,8 +29,8 @@ class ProximityInfo; class IncrementalDecoderWrapper : public IncrementalDecoderInterface { public: - IncrementalDecoderWrapper(const int maxWordLength, const int maxWords) { - mIncrementalDecoderInterface = getIncrementalDecoderInstance(maxWordLength, maxWords); + IncrementalDecoderWrapper(const int maxWordLength, const int maxWords) + : mIncrementalDecoderInterface(getIncrementalDecoderInstance(maxWordLength, maxWords)) { } virtual ~IncrementalDecoderWrapper() { diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp index 1b9bac0f0..e681f6f97 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/proximity_info.cpp @@ -29,6 +29,9 @@ namespace latinime { +/* static */ const int ProximityInfo::NOT_A_CODE = -1; +/* static */ const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f; + static inline void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray, jsize len, jint *buffer) { if (jArray && buffer) { @@ -54,16 +57,17 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma const jintArray keyWidths, const jintArray keyHeights, const jintArray keyCharCodes, const jfloatArray sweetSpotCenterXs, const jfloatArray sweetSpotCenterYs, const jfloatArray sweetSpotRadii) - : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), - KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), - MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), + : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), GRID_WIDTH(gridWidth), + GRID_HEIGHT(gridHeight), MOST_COMMON_KEY_WIDTH(mostCommonKeyWidth), MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates && keyWidths && keyHeights && keyCharCodes && sweetSpotCenterXs - && sweetSpotCenterYs && sweetSpotRadii) { + && sweetSpotCenterYs && sweetSpotRadii), + mProximityCharsArray(new int32_t[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE + /* proximityGridLength */]) { const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; if (DEBUG_PROXIMITY_INFO) { AKLOGI("Create proximity info array %d", proximityGridLength); @@ -75,7 +79,6 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, const int ma } memset(mLocaleStr, 0, sizeof(mLocaleStr)); env->GetStringUTFRegion(localeJStr, 0, env->GetStringLength(localeJStr), mLocaleStr); - mProximityCharsArray = new int32_t[proximityGridLength]; safeGetOrFillZeroIntArrayRegion(env, proximityChars, proximityGridLength, mProximityCharsArray); safeGetOrFillZeroIntArrayRegion(env, keyXCoordinates, KEY_COUNT, mKeyXCoordinates); safeGetOrFillZeroIntArrayRegion(env, keyYCoordinates, KEY_COUNT, mKeyYCoordinates); diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h index 7ee633ba8..822909b7a 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/proximity_info.h @@ -108,8 +108,8 @@ class ProximityInfo { static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64; // The upper limit of the char code in mCodeToKeyIndex static const int MAX_CHAR_CODE = 127; - static const float NOT_A_DISTANCE_FLOAT = -1.0f; - static const int NOT_A_CODE = -1; + static const int NOT_A_CODE; + static const float NOT_A_DISTANCE_FLOAT; int getStartIndexFromCoordinates(const int x, const int y) const; void initializeCodeToKeyIndex(); @@ -129,8 +129,6 @@ class ProximityInfo { } const int MAX_PROXIMITY_CHARS_SIZE; - const int KEYBOARD_WIDTH; - const int KEYBOARD_HEIGHT; const int GRID_WIDTH; const int GRID_HEIGHT; const int MOST_COMMON_KEY_WIDTH; diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h index 687a552c1..13b080705 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/proximity_info_state.h @@ -17,6 +17,7 @@ #ifndef LATINIME_PROXIMITY_INFO_STATE_H #define LATINIME_PROXIMITY_INFO_STATE_H +#include // for memset() #include #include #include @@ -49,7 +50,18 @@ class ProximityInfoState { ///////////////////////////////////////// // Defined here // ///////////////////////////////////////// - ProximityInfoState() {}; + ProximityInfoState() + : mProximityInfo(0), mMaxPointToKeyLength(0), + mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0), mLocaleStr(), + mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0), + mInputXs(), mInputYs(), mTimes(), mDistanceCache(), mLengthCache(), + mTouchPositionCorrectionEnabled(false), mInputSize(0) { + memset(mInputCodes, 0, sizeof(mInputCodes)); + memset(mNormalizedSquaredDistances, 0, sizeof(mNormalizedSquaredDistances)); + memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord)); + } + + virtual ~ProximityInfoState() {} inline unsigned short getPrimaryCharAt(const int index) const { return getProximityCharsAt(index)[0]; diff --git a/native/jni/src/terminal_attributes.h b/native/jni/src/terminal_attributes.h index 1ae9c7cbb..34ab8f0ef 100644 --- a/native/jni/src/terminal_attributes.h +++ b/native/jni/src/terminal_attributes.h @@ -30,13 +30,13 @@ class TerminalAttributes { public: class ShortcutIterator { const uint8_t *const mDict; - bool mHasNextShortcutTarget; int mPos; + bool mHasNextShortcutTarget; public: - ShortcutIterator(const uint8_t *dict, const int pos, const uint8_t flags) : mDict(dict), - mPos(pos) { - mHasNextShortcutTarget = (0 != (flags & BinaryFormat::FLAG_HAS_SHORTCUT_TARGETS)); + ShortcutIterator(const uint8_t *dict, const int pos, const uint8_t flags) + : mDict(dict), mPos(pos), + mHasNextShortcutTarget(0 != (flags & BinaryFormat::FLAG_HAS_SHORTCUT_TARGETS)) { } inline bool hasNextShortcutTarget() const { @@ -62,13 +62,6 @@ class TerminalAttributes { } }; - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalAttributes); - const uint8_t *const mDict; - const uint8_t mFlags; - const int mStartPos; - - public: TerminalAttributes(const uint8_t *const dict, const uint8_t flags, const int pos) : mDict(dict), mFlags(flags), mStartPos(pos) { } @@ -78,6 +71,12 @@ class TerminalAttributes { // skipped quickly, so we ignore it. return ShortcutIterator(mDict, mStartPos + BinaryFormat::SHORTCUT_LIST_SIZE_SIZE, mFlags); } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalAttributes); + const uint8_t *const mDict; + const uint8_t mFlags; + const int mStartPos; }; } // namespace latinime #endif // LATINIME_TERMINAL_ATTRIBUTES_H diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h index 1e4e00a23..19efa5da3 100644 --- a/native/jni/src/words_priority_queue.h +++ b/native/jni/src/words_priority_queue.h @@ -44,17 +44,16 @@ class WordsPriorityQueue { } }; - WordsPriorityQueue(int maxWords, int maxWordLength) : - MAX_WORDS((unsigned int) maxWords), MAX_WORD_LENGTH( - (unsigned int) maxWordLength) { - mSuggestedWords = new SuggestedWord[maxWordLength]; + WordsPriorityQueue(int maxWords, int maxWordLength) + : mSuggestions(), MAX_WORDS(static_cast(maxWords)), + MAX_WORD_LENGTH(static_cast(maxWordLength)), + mSuggestedWords(new SuggestedWord[maxWordLength]), mHighestSuggestedWord(0) { for (int i = 0; i < maxWordLength; ++i) { mSuggestedWords[i].mUsed = false; } - mHighestSuggestedWord = 0; } - ~WordsPriorityQueue() { + virtual ~WordsPriorityQueue() { delete[] mSuggestedWords; } diff --git a/native/jni/src/words_priority_queue_pool.h b/native/jni/src/words_priority_queue_pool.h index 38887291e..1112229da 100644 --- a/native/jni/src/words_priority_queue_pool.h +++ b/native/jni/src/words_priority_queue_pool.h @@ -24,9 +24,10 @@ namespace latinime { class WordsPriorityQueuePool { public: - WordsPriorityQueuePool(int mainQueueMaxWords, int subQueueMaxWords, int maxWordLength) { - // Note: using placement new() requires the caller to call the destructor explicitly. - mMasterQueue = new(mMasterQueueBuf) WordsPriorityQueue(mainQueueMaxWords, maxWordLength); + WordsPriorityQueuePool(int mainQueueMaxWords, int subQueueMaxWords, int maxWordLength) + // Note: using placement new() requires the caller to call the destructor explicitly. + : mMasterQueue(new(mMasterQueueBuf) WordsPriorityQueue( + mainQueueMaxWords, maxWordLength)) { for (int i = 0, subQueueBufOffset = 0; i < MULTIPLE_WORDS_SUGGESTION_MAX_WORDS * SUB_QUEUE_MAX_COUNT; ++i, subQueueBufOffset += sizeof(WordsPriorityQueue)) { @@ -88,8 +89,8 @@ class WordsPriorityQueuePool { WordsPriorityQueue *mMasterQueue; WordsPriorityQueue *mSubQueues[SUB_QUEUE_MAX_COUNT * MULTIPLE_WORDS_SUGGESTION_MAX_WORDS]; char mMasterQueueBuf[sizeof(WordsPriorityQueue)]; - char mSubQueueBuf[MULTIPLE_WORDS_SUGGESTION_MAX_WORDS - * SUB_QUEUE_MAX_COUNT * sizeof(WordsPriorityQueue)]; + char mSubQueueBuf[SUB_QUEUE_MAX_COUNT * MULTIPLE_WORDS_SUGGESTION_MAX_WORDS + * sizeof(WordsPriorityQueue)]; }; } // namespace latinime #endif // LATINIME_WORDS_PRIORITY_QUEUE_POOL_H From 0d63e6ea84a695f4b4af554aeedff1914a659209 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sat, 25 Aug 2012 00:03:16 +0900 Subject: [PATCH 122/133] Fix initialize order. Change-Id: I2686540bc6ed858e6d7ddc05a962c93714b94e1d --- native/jni/src/words_priority_queue_pool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/jni/src/words_priority_queue_pool.h b/native/jni/src/words_priority_queue_pool.h index 1112229da..c5de9797f 100644 --- a/native/jni/src/words_priority_queue_pool.h +++ b/native/jni/src/words_priority_queue_pool.h @@ -86,11 +86,11 @@ class WordsPriorityQueuePool { private: DISALLOW_IMPLICIT_CONSTRUCTORS(WordsPriorityQueuePool); - WordsPriorityQueue *mMasterQueue; - WordsPriorityQueue *mSubQueues[SUB_QUEUE_MAX_COUNT * MULTIPLE_WORDS_SUGGESTION_MAX_WORDS]; char mMasterQueueBuf[sizeof(WordsPriorityQueue)]; char mSubQueueBuf[SUB_QUEUE_MAX_COUNT * MULTIPLE_WORDS_SUGGESTION_MAX_WORDS * sizeof(WordsPriorityQueue)]; + WordsPriorityQueue *mMasterQueue; + WordsPriorityQueue *mSubQueues[SUB_QUEUE_MAX_COUNT * MULTIPLE_WORDS_SUGGESTION_MAX_WORDS]; }; } // namespace latinime #endif // LATINIME_WORDS_PRIORITY_QUEUE_POOL_H From 43da9ce041f1b622b23ec88a5606a162a99de0af Mon Sep 17 00:00:00 2001 From: Brian Muramatsu Date: Fri, 24 Aug 2012 10:25:10 -0700 Subject: [PATCH 123/133] Add mako keypress vibration and volume settings This prevents -1 and -1000 from appearing as the defaults in Advanced Settings. Change-Id: I42938782077a0a3dfb5ec2125c0266b6712bcf63 --- java/res/values/keypress-vibration-durations.xml | 1 + java/res/values/keypress-volumes.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml index 2569f2317..8f6b647b7 100644 --- a/java/res/values/keypress-vibration-durations.xml +++ b/java/res/values/keypress-vibration-durations.xml @@ -22,5 +22,6 @@ herring,5 tuna,5 + mako,20 diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml index 3b433e4ab..dd86d6c38 100644 --- a/java/res/values/keypress-volumes.xml +++ b/java/res/values/keypress-volumes.xml @@ -24,5 +24,6 @@ tuna,0.5 stingray,0.4 grouper,0.3 + mako,0.3 From 297f239196d78cf3bd8aca8adb86331a3f8d2139 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sun, 26 Aug 2012 11:30:59 +0900 Subject: [PATCH 124/133] Try using hash_map Change-Id: Icac47d03e1355e83b4dd3b0e3d7739f1dd5a3b98 --- native/jni/src/hash_map_compat.h | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 native/jni/src/hash_map_compat.h diff --git a/native/jni/src/hash_map_compat.h b/native/jni/src/hash_map_compat.h new file mode 100644 index 000000000..116359a73 --- /dev/null +++ b/native/jni/src/hash_map_compat.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef LATINIME_HASH_MAP_COMPAT_H +#define LATINIME_HASH_MAP_COMPAT_H + +// TODO: Use std::unordered_map that has been standardized in C++11 + +#ifdef __APPLE__ +#include +#else // __APPLE__ +#include +#endif // __APPLE__ + +#ifdef __SGI_STL_PORT +#define hash_map_compat stlport::hash_map +#else // __SGI_STL_PORT +#define hash_map_compat __gnu_cxx::hash_map +#endif // __SGI_STL_PORT + +#endif // LATINIME_HASH_MAP_COMPAT_H From a31d180cc31d2b661549178107bd9ba20175dcbe Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Mon, 27 Aug 2012 12:24:06 +0900 Subject: [PATCH 125/133] Change the definition of NOT_A_CHARACTER Change-Id: I519b1e5478abefb1c18cb6e3ab2862be045583de --- native/jni/src/defines.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 9b530077a..fdfe4ec71 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -211,7 +211,8 @@ static inline void prof_out(void) { #define DICTIONARY_VERSION_MIN 200 #define NOT_VALID_WORD -99 -#define NOT_A_CHARACTER -1 +// TODO: NOT_A_CHARACTER should be -1 +#define NOT_A_CHARACTER 0xFFFF // tentative #define NOT_A_DISTANCE -1 #define NOT_A_COORDINATE -1 #define EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO -2 From 1c2f33223995d8a6c069a85655f790388cd4e581 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 27 Aug 2012 14:43:39 +0900 Subject: [PATCH 126/133] Optimize gesture preview trail drawing a bit This change also change the fading out duration of the gesture preview trail to 800msec from 1,000msec Change-Id: Icfd5b1ecad66db4f978249486f5fe362a3ae9640 --- java/res/values/config.xml | 2 +- .../internal/GesturePreviewTrail.java | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 8477df054..8e2d43e4e 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -51,7 +51,7 @@ 70 200 100 - 1000 + 800 20 + 8dp diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 2fff73154..e42de0b4c 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -24,7 +24,6 @@ import android.support.v4.view.accessibility.AccessibilityEventCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; @@ -44,7 +43,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { /** * Inset in pixels to look for keys when the user's finger exits the - * keyboard area. See {@link ViewConfiguration#getScaledEdgeSlop()}. + * keyboard area. */ private int mEdgeSlop; @@ -62,7 +61,8 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { private void initInternal(InputMethodService inputMethod) { mInputMethod = inputMethod; - mEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop(); + mEdgeSlop = inputMethod.getResources().getDimensionPixelSize( + R.dimen.accessibility_edge_slop); } /** @@ -114,8 +114,14 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) { final int x = (int) event.getX(); final int y = (int) event.getY(); - final Key key = tracker.getKeyOn(x, y); final Key previousKey = mLastHoverKey; + final Key key; + + if (pointInView(x, y)) { + key = tracker.getKeyOn(x, y); + } else { + key = null; + } mLastHoverKey = key; @@ -123,7 +129,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { case MotionEvent.ACTION_HOVER_EXIT: // Make sure we're not getting an EXIT event because the user slid // off the keyboard area, then force a key press. - if (pointInView(x, y) && (key != null)) { + if (key != null) { getAccessibilityNodeProvider().simulateKeyPress(key); } //$FALL-THROUGH$ From ef2bfad5903fb55adca61dbea51984fbc7e4375f Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 28 Aug 2012 13:10:03 +0900 Subject: [PATCH 131/133] Use TypedArray.getFraction to read percentile values in resource Change-Id: Ifc8b024d190efba4cd536fd639621e841eb9970e --- java/res/values/attrs.xml | 12 +++--- java/res/values/dimens.xml | 2 +- java/res/values/styles.xml | 14 +++---- .../inputmethod/keyboard/KeyboardView.java | 31 ++++++++------- .../keyboard/MainKeyboardView.java | 4 +- .../suggestions/SuggestionStripView.java | 38 ++++++++----------- 6 files changed, 49 insertions(+), 52 deletions(-) diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index cced45d9f..de732fb63 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -187,13 +187,13 @@ - - - - - + + + + + - + diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index c59bad302..565059b10 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -92,7 +92,7 @@ 18dp 27dp 3 - 36 + 36% 2.5dp diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index e5e7fed80..ccf378a69 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -138,9 +138,9 @@ @android:color/white #FFFCAE00 #FFFCAE00 - 50 + 50% @integer/suggestions_count_in_strip - @integer/center_suggestion_percentile + @fraction/center_suggestion_percentile @integer/max_more_suggestions_row @fraction/min_more_suggestions_width @@ -373,12 +373,12 @@ @android:color/holo_blue_light @android:color/holo_blue_light @android:color/holo_blue_light - 85 - 85 - 70 - 70 + 85% + 85% + 70% + 70% @integer/suggestions_count_in_strip - @integer/center_suggestion_percentile + @fraction/center_suggestion_percentile @integer/max_more_suggestions_row @fraction/min_more_suggestions_width diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 0a70605d7..f233a8625 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -84,6 +84,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { // Miscellaneous constants private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; + private static final float UNDEFINED_RATIO = -1.0f; // XML attributes protected final float mVerticalCorrection; @@ -202,7 +203,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { private final float mKeyHintLetterRatio; private final float mKeyShiftedLetterHintRatio; private final float mKeyHintLabelRatio; - private static final float UNDEFINED_RATIO = -1.0f; public final Rect mPadding = new Rect(); public int mKeyLetterSize; @@ -220,20 +220,20 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mKeyLetterRatio = UNDEFINED_RATIO; mKeyLetterSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLetterSize, 0); } else { - mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio); + mKeyLetterRatio = getFraction(a, R.styleable.KeyboardView_keyLetterRatio); } if (a.hasValue(R.styleable.KeyboardView_keyLabelSize)) { mKeyLabelRatio = UNDEFINED_RATIO; mKeyLabelSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLabelSize, 0); } else { - mKeyLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLabelRatio); + mKeyLabelRatio = getFraction(a, R.styleable.KeyboardView_keyLabelRatio); } - mKeyLargeLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLabelRatio); - mKeyLargeLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLetterRatio); - mKeyHintLetterRatio = getRatio(a, R.styleable.KeyboardView_keyHintLetterRatio); - mKeyShiftedLetterHintRatio = getRatio(a, + mKeyLargeLabelRatio = getFraction(a, R.styleable.KeyboardView_keyLargeLabelRatio); + mKeyLargeLetterRatio = getFraction(a, R.styleable.KeyboardView_keyLargeLetterRatio); + mKeyHintLetterRatio = getFraction(a, R.styleable.KeyboardView_keyHintLetterRatio); + mKeyShiftedLetterHintRatio = getFraction(a, R.styleable.KeyboardView_keyShiftedLetterHintRatio); - mKeyHintLabelRatio = getRatio(a, R.styleable.KeyboardView_keyHintLabelRatio); + mKeyHintLabelRatio = getFraction(a, R.styleable.KeyboardView_keyHintLabelRatio); mKeyLabelHorizontalPadding = a.getDimension( R.styleable.KeyboardView_keyLabelHorizontalPadding, 0); mKeyHintLetterPadding = a.getDimension( @@ -260,10 +260,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } public void updateKeyHeight(int keyHeight) { - if (mKeyLetterRatio >= 0.0f) { + if (isValidFraction(mKeyLetterRatio)) { mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); } - if (mKeyLabelRatio >= 0.0f) { + if (isValidFraction(mKeyLabelRatio)) { mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio); } mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio); @@ -338,7 +338,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { R.styleable.KeyboardView_keyPreviewOffset, 0); mPreviewHeight = a.getDimensionPixelSize( R.styleable.KeyboardView_keyPreviewHeight, 80); - mPreviewTextRatio = getRatio(a, R.styleable.KeyboardView_keyPreviewTextRatio); + mPreviewTextRatio = getFraction(a, R.styleable.KeyboardView_keyPreviewTextRatio); mPreviewTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0); mLingerTimeout = a.getInt(R.styleable.KeyboardView_keyPreviewLingerTimeout, 0); @@ -387,9 +387,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { mPaint.setAntiAlias(true); } - // Read fraction value in TypedArray as float. - /* package */ static float getRatio(TypedArray a, int index) { - return a.getFraction(index, 1000, 1000, 1) / 1000.0f; + static boolean isValidFraction(final float fraction) { + return fraction >= 0.0f; + } + + static float getFraction(final TypedArray a, final int index) { + return a.getFraction(index, 1, 1, UNDEFINED_RATIO); } /** diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 0cc0b6320..df84271e8 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -344,8 +344,8 @@ public class MainKeyboardView extends KeyboardView implements PointerTracker.Key R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false); mAutoCorrectionSpacebarLedIcon = a.getDrawable( R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon); - mSpacebarTextRatio = a.getFraction(R.styleable.MainKeyboardView_spacebarTextRatio, - 1000, 1000, 1) / 1000.0f; + mSpacebarTextRatio = a.getFraction( + R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f); mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0); mSpacebarTextShadowColor = a.getColor( R.styleable.MainKeyboardView_spacebarTextShadowColor, 0); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index afc4293c0..03263d274 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -132,7 +132,7 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen private static class SuggestionStripViewParams { private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; - private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40; + private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f; private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; private static final int PUNCTUATIONS_IN_STRIP = 5; @@ -196,16 +196,16 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); mSuggestionStripOption = a.getInt( R.styleable.SuggestionStripView_suggestionStripOption, 0); - final float alphaValidTypedWord = getPercent(a, - R.styleable.SuggestionStripView_alphaValidTypedWord, 100); - final float alphaTypedWord = getPercent(a, - R.styleable.SuggestionStripView_alphaTypedWord, 100); - final float alphaAutoCorrect = getPercent(a, - R.styleable.SuggestionStripView_alphaAutoCorrect, 100); - final float alphaSuggested = getPercent(a, - R.styleable.SuggestionStripView_alphaSuggested, 100); - mAlphaObsoleted = getPercent(a, - R.styleable.SuggestionStripView_alphaSuggested, 100); + final float alphaValidTypedWord = getFraction(a, + R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f); + final float alphaTypedWord = getFraction(a, + R.styleable.SuggestionStripView_alphaTypedWord, 1.0f); + final float alphaAutoCorrect = getFraction(a, + R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f); + final float alphaSuggested = getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); + mAlphaObsoleted = getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); mColorValidTypedWord = applyAlpha(a.getColor( R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord); mColorTypedWord = applyAlpha(a.getColor( @@ -217,14 +217,14 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen mSuggestionsCountInStrip = a.getInt( R.styleable.SuggestionStripView_suggestionsCountInStrip, DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); - mCenterSuggestionWeight = getPercent(a, + mCenterSuggestionWeight = getFraction(a, R.styleable.SuggestionStripView_centerSuggestionPercentile, DEFAULT_CENTER_SUGGESTION_PERCENTILE); mMaxMoreSuggestionsRow = a.getInt( R.styleable.SuggestionStripView_maxMoreSuggestionsRow, DEFAULT_MAX_MORE_SUGGESTIONS_ROW); - mMinMoreSuggestionsWidth = getRatio(a, - R.styleable.SuggestionStripView_minMoreSuggestionsWidth); + mMinMoreSuggestionsWidth = getFraction(a, + R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f); a.recycle(); mMoreSuggestionsHint = getMoreSuggestionsHint(res, @@ -278,14 +278,8 @@ public class SuggestionStripView extends RelativeLayout implements OnClickListen return new BitmapDrawable(res, buffer); } - // Read integer value in TypedArray as percent. - private static float getPercent(TypedArray a, int index, int defValue) { - return a.getInt(index, defValue) / 100.0f; - } - - // Read fraction value in TypedArray as float. - private static float getRatio(TypedArray a, int index) { - return a.getFraction(index, 1000, 1000, 1) / 1000.0f; + static float getFraction(final TypedArray a, final int index, final float defValue) { + return a.getFraction(index, 1, 1, defValue); } private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) { From 9fa0736d6ac8a171a5f9620a5d0980dc01dc65a2 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 28 Aug 2012 12:39:28 +0900 Subject: [PATCH 132/133] Consolidate KeyboardView.keyLetterSize and keyLetterRatio This change also consolidates keyboardView.keyLabelSize and keyLabelRatio. Change-Id: I4a45bcb6e7fc104ae4a9ae3ecdae9842d813840e --- java/res/values/attrs.xml | 30 +++++++++--------- java/res/values/styles.xml | 4 +-- .../inputmethod/keyboard/KeyboardView.java | 31 ++++++++++++------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index de732fb63..76e76cc82 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -41,26 +41,24 @@ checkable+checked+pressed. --> - - - - - - + + + + - - - + - + - + - + - + + @@ -96,8 +94,8 @@ - - + + diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index ccf378a69..0220c836e 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -35,9 +35,9 @@