From 928ebfeaf867341fd4a0ee03b0cf55e006688f1a Mon Sep 17 00:00:00 2001 From: satok Date: Tue, 1 Jun 2010 19:58:36 +0900 Subject: [PATCH] Add aggressive cancellation for auto suggestion - Add ring buffer - Count separator for auto suggestion - Add a test for ring buffer Change-Id: Id4a0aa00ceb1b055b8fc96c45e100d318cceb2ab --- java/AndroidManifest.xml | 2 +- .../android/inputmethod/latin/LatinIME.java | 6 +- .../inputmethod/latin/LatinImeLogger.java | 134 ++++++++++++++---- tests/Android.mk | 2 +- tests/AndroidManifest.xml | 2 +- .../inputmethod/latin/ImeLoggerTests.java | 59 ++++++++ .../inputmethod/latin/tests/SuggestTests.java | 9 +- 7 files changed, 176 insertions(+), 38 deletions(-) create mode 100644 tests/src/com/android/inputmethod/latin/ImeLoggerTests.java diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 10be866af..b4a5ab60c 100755 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionName="0.14"> diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b15de6bd5..35edb8ae7 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -219,7 +219,7 @@ public class LatinIME extends InputMethodService private final float FX_VOLUME = -1.0f; private boolean mSilentMode; - private String mWordSeparators; + /* package */ String mWordSeparators; private String mSentenceSeparators; private VoiceInput mVoiceInput; private VoiceResults mVoiceResults = new VoiceResults(); @@ -955,7 +955,7 @@ public class LatinIME extends InputMethodService case Keyboard.KEYCODE_DELETE: handleBackspace(); mDeleteCount++; - LatinImeLogger.logOnDelete(1); + LatinImeLogger.logOnDelete(); break; case Keyboard.KEYCODE_SHIFT: handleShift(); @@ -996,12 +996,12 @@ public class LatinIME extends InputMethodService if (primaryCode != KEYCODE_ENTER) { mJustAddedAutoSpace = false; } + LatinImeLogger.logOnInputChar((char)primaryCode); if (isWordSeparator(primaryCode)) { handleSeparator(primaryCode); } else { handleCharacter(primaryCode, keyCodes); } - LatinImeLogger.logOnInputChar(1); // Cancel the just reverted state mJustRevertedSeparator = null; } diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index b497c0c66..736b0af54 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -33,7 +33,8 @@ import java.util.Collections; public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = "LatinIMELogs"; - private static boolean sDBG = false; + private static final boolean DBG = true; + private static boolean sLOGPRINT = false; // SUPPRESS_EXCEPTION should be true when released to public. private static final boolean SUPPRESS_EXCEPTION = false; // DEFAULT_LOG_ENABLED should be false when released to public. @@ -43,6 +44,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang private static final long MINIMUMCOUNTINTERVAL = 20 * DateUtils.SECOND_IN_MILLIS; // 20 sec private static final long MINIMUMSENDSIZE = 40; private static final char SEPARATER = ';'; + private static final char NULL_CHAR = '\uFFFC'; private static final int ID_CLICKSUGGESTION = 0; private static final int ID_AUTOSUGGESTIONCANCELLED = 1; private static final int ID_AUTOSUGGESTION = 2; @@ -60,14 +62,17 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang private static final String PREF_AUTO_COMPLETE = "auto_complete"; public static boolean sLogEnabled = true; - private static LatinImeLogger sLatinImeLogger = new LatinImeLogger(); + /* package */ static LatinImeLogger sLatinImeLogger = new LatinImeLogger(); // Store the last auto suggested word. // This is required for a cancellation log of auto suggestion of that word. - private static String sLastAutoSuggestBefore; - private static String sLastAutoSuggestAfter; + /* package */ static String sLastAutoSuggestBefore; + /* package */ static String sLastAutoSuggestAfter; + /* package */ static String sLastAutoSuggestSeparator; private ArrayList mLogBuffer = null; private ArrayList mPrivacyLogBuffer = null; + /* package */ RingCharBuffer mRingCharBuffer = null; + private Context mContext = null; private DropBoxManager mDropBox = null; private long mLastTimeActive; @@ -116,11 +121,12 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang mActualCharCount = 0; mLogBuffer = new ArrayList(); mPrivacyLogBuffer = new ArrayList(); + mRingCharBuffer = new RingCharBuffer(context); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); sLogEnabled = prefs.getBoolean(PREF_ENABLE_LOG, DEFAULT_LOG_ENABLED); mThemeId = prefs.getString(KeyboardSwitcher.PREF_KEYBOARD_LAYOUT, KeyboardSwitcher.DEFAULT_LAYOUT_ID); - sDBG = prefs.getBoolean(PREF_DEBUG_MODE, sDBG); + sLOGPRINT = prefs.getBoolean(PREF_DEBUG_MODE, sLOGPRINT); prefs.registerOnSharedPreferenceChangeListener(this); } @@ -134,13 +140,14 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang mActualCharCount = 0; mLogBuffer.clear(); mPrivacyLogBuffer.clear(); + mRingCharBuffer.reset(); } /** * Check if the input string is safe as an entry or not. */ private static boolean checkStringDataSafe(String s) { - if (sDBG) { + if (DBG) { Log.d(TAG, "Check String safety: " + s); } for (int i = 0; i < s.length(); ++i) { @@ -152,7 +159,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void addCountEntry(long time) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Log counts. (4)"); } mLogBuffer.add(new LogEntry (time, ID_DELETE_COUNT, @@ -171,7 +178,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void addThemeIdEntry(long time) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Log theme Id. (1)"); } mLogBuffer.add(new LogEntry (time, ID_THEME_ID, @@ -179,7 +186,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void addSettingsEntry(long time) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Log settings. (1)"); } final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); @@ -189,7 +196,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void addVersionNameEntry(long time) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Log Version. (1)"); } try { @@ -203,15 +210,15 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void addExceptionEntry(long time, String[] data) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Log Exception. (1)"); } mLogBuffer.add(new LogEntry(time, ID_EXCEPTION, data)); } private void flushPrivacyLogSafely() { - if (sDBG) { - Log.d(TAG, "Log theme Id. (" + mPrivacyLogBuffer.size() + ")"); + if (sLOGPRINT) { + Log.d(TAG, "Log obfuscated data. (" + mPrivacyLogBuffer.size() + ")"); } long now = System.currentTimeMillis(); Collections.sort(mPrivacyLogBuffer); @@ -248,7 +255,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang ++mWordCount; String[] dataStrings = (String[]) data; if (dataStrings.length < 2) { - if (sDBG) { + if (DBG) { Log.e(TAG, "The length of logged string array is invalid."); } break; @@ -258,7 +265,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang mPrivacyLogBuffer.add( new LogEntry (System.currentTimeMillis(), tag, dataStrings)); } else { - if (sDBG) { + if (DBG) { Log.d(TAG, "Skipped to add an entry because data is unsafe."); } } @@ -267,7 +274,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang --mWordCount; dataStrings = (String[]) data; if (dataStrings.length < 2) { - if (sDBG) { + if (DBG) { Log.e(TAG, "The length of logged string array is invalid."); } break; @@ -277,7 +284,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang mPrivacyLogBuffer.add( new LogEntry (System.currentTimeMillis(), tag, dataStrings)); } else { - if (sDBG) { + if (DBG) { Log.d(TAG, "Skipped to add an entry because data is unsafe."); } } @@ -285,7 +292,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang case ID_EXCEPTION: dataStrings = (String[]) data; if (dataStrings.length < 2) { - if (sDBG) { + if (DBG) { Log.e(TAG, "The length of logged string array is invalid."); } break; @@ -293,7 +300,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang addExceptionEntry(System.currentTimeMillis(), dataStrings); break; default: - if (sDBG) { + if (DBG) { Log.e(TAG, "Log Tag is not entried."); } break; @@ -301,7 +308,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang } private void commitInternal() { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Commit (" + mLogBuffer.size() + ")"); } flushPrivacyLogSafely(); @@ -312,7 +319,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang addVersionNameEntry(now); String s = LogSerializer.createStringFromEntries(mLogBuffer); if (!TextUtils.isEmpty(s)) { - if (sDBG) { + if (sLOGPRINT) { Log.d(TAG, "Commit log: " + s); } mDropBox.addText(TAG, s); @@ -329,7 +336,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void sendLogToDropBox(int tag, Object s) { long now = System.currentTimeMillis(); - if (sDBG) { + if (DBG) { String out = ""; if (s instanceof String[]) { for (String str: ((String[]) s)) { @@ -367,7 +374,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang KeyboardSwitcher.DEFAULT_LAYOUT_ID); addThemeIdEntry(mLastTimeActive); } else if (PREF_DEBUG_MODE.equals(key)) { - sDBG = sharedPreferences.getBoolean(PREF_DEBUG_MODE, sDBG); + sLOGPRINT = sharedPreferences.getBoolean(PREF_DEBUG_MODE, sLOGPRINT); } } @@ -403,7 +410,9 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang before = ""; after = ""; } - String[] strings = new String[] {before, after}; + sLastAutoSuggestSeparator = + String.valueOf(sLatinImeLogger.mRingCharBuffer.getLastChar()); + String[] strings = new String[] {before, after, sLastAutoSuggestSeparator}; synchronized (LatinImeLogger.class) { sLastAutoSuggestBefore = before; sLastAutoSuggestAfter = after; @@ -415,21 +424,33 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static void logOnAutoSuggestionCanceled() { if (sLogEnabled) { if (sLastAutoSuggestBefore != null && sLastAutoSuggestAfter != null) { - String[] strings = new String[] {sLastAutoSuggestBefore, sLastAutoSuggestAfter}; + String[] strings = new String[] { + sLastAutoSuggestBefore, sLastAutoSuggestAfter, sLastAutoSuggestSeparator}; sLatinImeLogger.sendLogToDropBox(ID_AUTOSUGGESTIONCANCELLED, strings); } + synchronized (LatinImeLogger.class) { + sLastAutoSuggestBefore = ""; + sLastAutoSuggestAfter = ""; + } } } - public static void logOnDelete(int length) { + public static void logOnDelete() { if (sLogEnabled) { - sLatinImeLogger.sendLogToDropBox(ID_DELETE_COUNT, length); + String mLastWord = sLatinImeLogger.mRingCharBuffer.getLastString(); + if (!TextUtils.isEmpty(mLastWord) + && mLastWord.equalsIgnoreCase(sLastAutoSuggestBefore)) { + logOnAutoSuggestionCanceled(); + } + sLatinImeLogger.mRingCharBuffer.pop(); + sLatinImeLogger.sendLogToDropBox(ID_DELETE_COUNT, 1); } } - public static void logOnInputChar(int length) { + public static void logOnInputChar(char c) { if (sLogEnabled) { - sLatinImeLogger.sendLogToDropBox(ID_INPUT_COUNT, length); + sLatinImeLogger.mRingCharBuffer.push(c); + sLatinImeLogger.sendLogToDropBox(ID_INPUT_COUNT, 1); } } @@ -478,4 +499,59 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang return sb.toString(); } } + + /* package */ static class RingCharBuffer { + final int BUFSIZE = 20; + private Context mContext; + private int mEnd = 0; + /* package */ int length = 0; + private char[] mCharBuf = new char[BUFSIZE]; + + public RingCharBuffer(Context context) { + mContext = context; + } + + private int normalize(int in) { + int ret = in % BUFSIZE; + return ret < 0 ? ret + BUFSIZE : ret; + } + public void push(char c) { + mCharBuf[mEnd] = c; + mEnd = normalize(mEnd + 1); + if (length < BUFSIZE) { + ++length; + } + } + public char pop() { + if (length < 1) { + return NULL_CHAR; + } else { + mEnd = normalize(mEnd - 1); + --length; + return mCharBuf[mEnd]; + } + } + public char getLastChar() { + if (length < 1) { + return NULL_CHAR; + } else { + return mCharBuf[normalize(mEnd - 1)]; + } + } + public String getLastString() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; ++i) { + char c = mCharBuf[normalize(mEnd - 1 - i)]; + if (!((LatinIME)mContext).isWordSeparator(c)) { + sb.append(c); + } else { + break; + } + } + return sb.reverse().toString(); + } + public void reset() { + length = 0; + } + } } diff --git a/tests/Android.mk b/tests/Android.mk index e72587ddd..60e82d5c2 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -10,7 +10,7 @@ LOCAL_JAVA_LIBRARIES := android.test.runner # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_PACKAGE_NAME := LatinIMETests +LOCAL_PACKAGE_NAME := LatinIME2Tests LOCAL_INSTRUMENTATION_FOR := LatinIme2Google diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 210e81489..66cecee8b 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -27,7 +27,7 @@ diff --git a/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java new file mode 100644 index 000000000..234559bb7 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java @@ -0,0 +1,59 @@ +package com.android.inputmethod.latin; + +import android.test.ServiceTestCase; + +public class ImeLoggerTests extends ServiceTestCase { + + private static final String WORD_SEPARATORS + = ".\u0009\u0020,;:!?\n()[]*&@{}<>;_+=|\\u0022"; + + public ImeLoggerTests() { + super(LatinIME.class); + } + static LatinImeLogger sLogger; + @Override + protected void setUp() { + try { + super.setUp(); + } catch (Exception e) { + e.printStackTrace(); + } + setupService(); + // startService(null); // can't be started because VoiceInput can't be found. + final LatinIME context = getService(); + context.mWordSeparators = WORD_SEPARATORS; + LatinImeLogger.init(context); + sLogger = LatinImeLogger.sLatinImeLogger; + } + /*********************** Tests *********************/ + public void testRingBuffer() { + for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) { + LatinImeLogger.logOnDelete(); + } + assertEquals("", sLogger.mRingCharBuffer.getLastString()); + LatinImeLogger.logOnInputChar('t'); + LatinImeLogger.logOnInputChar('g'); + LatinImeLogger.logOnInputChar('i'); + LatinImeLogger.logOnInputChar('s'); + LatinImeLogger.logOnInputChar(' '); + LatinImeLogger.logOnAutoSuggestion("tgis", "this"); + LatinImeLogger.logOnInputChar(' '); + LatinImeLogger.logOnDelete(); + assertEquals("", sLogger.mRingCharBuffer.getLastString()); + LatinImeLogger.logOnDelete(); + assertEquals("tgis", sLogger.mRingCharBuffer.getLastString()); + assertEquals("tgis", LatinImeLogger.sLastAutoSuggestBefore); + LatinImeLogger.logOnAutoSuggestionCanceled(); + assertEquals("", LatinImeLogger.sLastAutoSuggestBefore); + LatinImeLogger.logOnDelete(); + assertEquals("tgi", sLogger.mRingCharBuffer.getLastString()); + for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) { + LatinImeLogger.logOnDelete(); + } + assertEquals("", sLogger.mRingCharBuffer.getLastString()); + for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) { + LatinImeLogger.logOnInputChar('a'); + } + assertEquals(sLogger.mRingCharBuffer.BUFSIZE, sLogger.mRingCharBuffer.length); + } +} diff --git a/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java index 9401d926a..0d3babfbc 100644 --- a/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java +++ b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java @@ -241,8 +241,11 @@ public class SuggestTests extends AndroidTestCase { * Are accented forms of words suggested as corrections? */ public void testAccents() { - assertTrue(isDefaultCorrection("nino", "ni\u00F1o")); // ni–o - assertTrue(isDefaultCorrection("nimo", "ni\u00F1o")); // ni–o - assertTrue(isDefaultCorrection("maria", "Mar\u00EDa")); // Mar’a + // nio + assertTrue(isDefaultCorrection("nino", "ni\u00F1o")); + // nio + assertTrue(isDefaultCorrection("nimo", "ni\u00F1o")); + // Mara + assertTrue(isDefaultCorrection("maria", "Mar\u00EDa")); } }