aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/research
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/research')
-rw-r--r--java/src/com/android/inputmethod/research/JsonUtils.java103
-rw-r--r--java/src/com/android/inputmethod/research/LogBuffer.java4
-rw-r--r--java/src/com/android/inputmethod/research/LogUnit.java119
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLog.java105
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java364
-rw-r--r--java/src/com/android/inputmethod/research/Statistics.java34
-rw-r--r--java/src/com/android/inputmethod/research/UploaderService.java19
7 files changed, 589 insertions, 159 deletions
diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java
new file mode 100644
index 000000000..cb331d7f9
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/JsonUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.SharedPreferences;
+import android.util.JsonWriter;
+import android.view.inputmethod.CompletionInfo;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.io.IOException;
+import java.util.Map;
+
+/* package */ class JsonUtils {
+ private JsonUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ /* package */ static void writeJson(final CompletionInfo[] ci, final JsonWriter jsonWriter)
+ throws IOException {
+ jsonWriter.beginArray();
+ for (int j = 0; j < ci.length; j++) {
+ jsonWriter.value(ci[j].toString());
+ }
+ jsonWriter.endArray();
+ }
+
+ /* package */ static void writeJson(final SharedPreferences prefs, final JsonWriter jsonWriter)
+ throws IOException {
+ jsonWriter.beginObject();
+ for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
+ jsonWriter.name(entry.getKey());
+ final Object innerValue = entry.getValue();
+ if (innerValue == null) {
+ jsonWriter.nullValue();
+ } else if (innerValue instanceof Boolean) {
+ jsonWriter.value((Boolean) innerValue);
+ } else if (innerValue instanceof Number) {
+ jsonWriter.value((Number) innerValue);
+ } else {
+ jsonWriter.value(innerValue.toString());
+ }
+ }
+ jsonWriter.endObject();
+ }
+
+ /* package */ static void writeJson(final Key[] keys, final JsonWriter jsonWriter)
+ throws IOException {
+ jsonWriter.beginArray();
+ for (Key key : keys) {
+ writeJson(key, jsonWriter);
+ }
+ jsonWriter.endArray();
+ }
+
+ private static void writeJson(final Key key, final JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("code").value(key.mCode);
+ jsonWriter.name("altCode").value(key.getAltCode());
+ jsonWriter.name("x").value(key.mX);
+ jsonWriter.name("y").value(key.mY);
+ jsonWriter.name("w").value(key.mWidth);
+ jsonWriter.name("h").value(key.mHeight);
+ jsonWriter.endObject();
+ }
+
+ /* package */ static void writeJson(final SuggestedWords words, final JsonWriter jsonWriter)
+ throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("typedWordValid").value(words.mTypedWordValid);
+ jsonWriter.name("willAutoCorrect")
+ .value(words.mWillAutoCorrect);
+ jsonWriter.name("isPunctuationSuggestions")
+ .value(words.mIsPunctuationSuggestions);
+ jsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
+ jsonWriter.name("isPrediction").value(words.mIsPrediction);
+ jsonWriter.name("words");
+ jsonWriter.beginArray();
+ final int size = words.size();
+ for (int j = 0; j < size; j++) {
+ final SuggestedWordInfo wordInfo = words.getWordInfo(j);
+ jsonWriter.value(wordInfo.toString());
+ }
+ jsonWriter.endArray();
+ jsonWriter.endObject();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java
index ae7b1579a..a3c3e89de 100644
--- a/java/src/com/android/inputmethod/research/LogBuffer.java
+++ b/java/src/com/android/inputmethod/research/LogBuffer.java
@@ -110,4 +110,8 @@ public class LogBuffer {
}
return logUnit;
}
+
+ public boolean isEmpty() {
+ return mLogUnits.isEmpty();
+ }
}
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index ab9d2f857..27c4027de 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -16,10 +16,23 @@
package com.android.inputmethod.research;
+import android.content.SharedPreferences;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.view.inputmethod.CompletionInfo;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger.LogStatement;
+import java.io.IOException;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* A group of log statements related to each other.
@@ -36,6 +49,8 @@ import java.util.List;
* been published recently, or whether the LogUnit contains numbers, etc.
*/
/* package */ class LogUnit {
+ private static final String TAG = LogUnit.class.getSimpleName();
+ private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
private final ArrayList<LogStatement> mLogStatementList;
private final ArrayList<Object[]> mValuesList;
// Assume that mTimeList is sorted in increasing order. Do not insert null values into
@@ -77,8 +92,29 @@ import java.util.List;
mTimeList.add(time);
}
- public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ /**
+ * Publish the contents of this LogUnit to researchLog.
+ */
+ public synchronized void publishTo(final ResearchLog researchLog,
+ final boolean isIncludingPrivateData) {
+ // Prepare debugging output if necessary
+ final StringWriter debugStringWriter;
+ final JsonWriter debugJsonWriter;
+ if (DEBUG) {
+ debugStringWriter = new StringWriter();
+ debugJsonWriter = new JsonWriter(debugStringWriter);
+ debugJsonWriter.setIndent(" ");
+ try {
+ debugJsonWriter.beginArray();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not open array in JsonWriter", e);
+ }
+ } else {
+ debugStringWriter = null;
+ debugJsonWriter = null;
+ }
final int size = mLogStatementList.size();
+ // Write out any logStatement that passes the privacy filter.
for (int i = 0; i < size; i++) {
final LogStatement logStatement = mLogStatementList.get(i);
if (!isIncludingPrivateData && logStatement.mIsPotentiallyPrivate) {
@@ -87,8 +123,87 @@ import java.util.List;
if (mIsPartOfMegaword && logStatement.mIsPotentiallyRevealing) {
continue;
}
- researchLog.outputEvent(mLogStatementList.get(i), mValuesList.get(i), mTimeList.get(i));
+ // Only retrieve the jsonWriter if we need to. If we don't get this far, then
+ // researchLog.getValidJsonWriter() will not open the file for writing.
+ final JsonWriter jsonWriter = researchLog.getValidJsonWriterLocked();
+ outputLogStatementToLocked(jsonWriter, mLogStatementList.get(i), mValuesList.get(i),
+ mTimeList.get(i));
+ if (DEBUG) {
+ outputLogStatementToLocked(debugJsonWriter, mLogStatementList.get(i),
+ mValuesList.get(i), mTimeList.get(i));
+ }
+ }
+ if (DEBUG) {
+ try {
+ debugJsonWriter.endArray();
+ debugJsonWriter.flush();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not close array in JsonWriter", e);
+ }
+ final String bigString = debugStringWriter.getBuffer().toString();
+ final String[] lines = bigString.split("\n");
+ for (String line : lines) {
+ Log.d(TAG, line);
+ }
+ }
+ }
+
+ private static final String CURRENT_TIME_KEY = "_ct";
+ private static final String UPTIME_KEY = "_ut";
+ private static final String EVENT_TYPE_KEY = "_ty";
+
+ /**
+ * Write the logStatement and its contents out through jsonWriter.
+ *
+ * Note that this method is not thread safe for the same jsonWriter. Callers must ensure
+ * thread safety.
+ */
+ private boolean outputLogStatementToLocked(final JsonWriter jsonWriter,
+ final LogStatement logStatement, final Object[] values, final Long time) {
+ if (DEBUG) {
+ if (logStatement.mKeys.length != values.length) {
+ Log.d(TAG, "Key and Value list sizes do not match. " + logStatement.mName);
+ }
+ }
+ try {
+ jsonWriter.beginObject();
+ jsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
+ jsonWriter.name(UPTIME_KEY).value(time);
+ jsonWriter.name(EVENT_TYPE_KEY).value(logStatement.mName);
+ final String[] keys = logStatement.mKeys;
+ final int length = values.length;
+ for (int i = 0; i < length; i++) {
+ jsonWriter.name(keys[i]);
+ final Object value = values[i];
+ if (value instanceof CharSequence) {
+ jsonWriter.value(value.toString());
+ } else if (value instanceof Number) {
+ jsonWriter.value((Number) value);
+ } else if (value instanceof Boolean) {
+ jsonWriter.value((Boolean) value);
+ } else if (value instanceof CompletionInfo[]) {
+ JsonUtils.writeJson((CompletionInfo[]) value, jsonWriter);
+ } else if (value instanceof SharedPreferences) {
+ JsonUtils.writeJson((SharedPreferences) value, jsonWriter);
+ } else if (value instanceof Key[]) {
+ JsonUtils.writeJson((Key[]) value, jsonWriter);
+ } else if (value instanceof SuggestedWords) {
+ JsonUtils.writeJson((SuggestedWords) value, jsonWriter);
+ } else if (value == null) {
+ jsonWriter.nullValue();
+ } else {
+ Log.w(TAG, "Unrecognized type to be logged: " +
+ (value == null ? "<null>" : value.getClass().getName()));
+ jsonWriter.nullValue();
+ }
+ }
+ jsonWriter.endObject();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.w(TAG, "Error in JsonWriter; skipping LogStatement");
+ return false;
}
+ return true;
}
public void setWord(String word) {
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
index 3c8731995..a6b1b889f 100644
--- a/java/src/com/android/inputmethod/research/ResearchLog.java
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -16,17 +16,10 @@
package com.android.inputmethod.research;
-import android.content.SharedPreferences;
-import android.os.SystemClock;
import android.util.JsonWriter;
import android.util.Log;
-import android.view.inputmethod.CompletionInfo;
-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.LogStatement;
import java.io.BufferedWriter;
import java.io.File;
@@ -34,7 +27,6 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
@@ -204,103 +196,17 @@ public class ResearchLog {
}
}
- 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 LogStatement logStatement, final Object[] values, final long time) {
- // Not thread safe.
- if (DEBUG) {
- if (logStatement.mKeys.length != values.length) {
- Log.d(TAG, "Key and Value list sizes do not match. " + logStatement.mName);
- }
- }
+ /**
+ * Return a JsonWriter for this ResearchLog. It is initialized the first time this method is
+ * called. The cached value is returned in future calls.
+ */
+ public JsonWriter getValidJsonWriterLocked() {
try {
if (mJsonWriter == NULL_JSON_WRITER) {
mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
mJsonWriter.beginArray();
mHasWrittenData = true;
}
- mJsonWriter.beginObject();
- mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
- mJsonWriter.name(UPTIME_KEY).value(time);
- mJsonWriter.name(EVENT_TYPE_KEY).value(logStatement.mName);
- final String[] keys = logStatement.mKeys;
- final int length = values.length;
- for (int i = 0; i < length; i++) {
- mJsonWriter.name(keys[i]);
- Object value = values[i];
- if (value instanceof CharSequence) {
- mJsonWriter.value(value.toString());
- } else if (value instanceof Number) {
- mJsonWriter.value((Number) value);
- } else if (value instanceof Boolean) {
- mJsonWriter.value((Boolean) value);
- } else if (value instanceof CompletionInfo[]) {
- CompletionInfo[] ci = (CompletionInfo[]) value;
- mJsonWriter.beginArray();
- for (int j = 0; j < ci.length; j++) {
- mJsonWriter.value(ci[j].toString());
- }
- mJsonWriter.endArray();
- } else if (value instanceof SharedPreferences) {
- SharedPreferences prefs = (SharedPreferences) value;
- mJsonWriter.beginObject();
- for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
- mJsonWriter.name(entry.getKey());
- final Object innerValue = entry.getValue();
- if (innerValue == null) {
- mJsonWriter.nullValue();
- } else if (innerValue instanceof Boolean) {
- mJsonWriter.value((Boolean) innerValue);
- } else if (innerValue instanceof Number) {
- mJsonWriter.value((Number) innerValue);
- } else {
- mJsonWriter.value(innerValue.toString());
- }
- }
- mJsonWriter.endObject();
- } else if (value instanceof Key[]) {
- Key[] keyboardKeys = (Key[]) value;
- mJsonWriter.beginArray();
- for (Key keyboardKey : keyboardKeys) {
- mJsonWriter.beginObject();
- mJsonWriter.name("code").value(keyboardKey.mCode);
- mJsonWriter.name("altCode").value(keyboardKey.getAltCode());
- mJsonWriter.name("x").value(keyboardKey.mX);
- mJsonWriter.name("y").value(keyboardKey.mY);
- mJsonWriter.name("w").value(keyboardKey.mWidth);
- mJsonWriter.name("h").value(keyboardKey.mHeight);
- mJsonWriter.endObject();
- }
- mJsonWriter.endArray();
- } else if (value instanceof SuggestedWords) {
- SuggestedWords words = (SuggestedWords) value;
- mJsonWriter.beginObject();
- mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
- mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
- mJsonWriter.name("isPunctuationSuggestions")
- .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();
- for (int j = 0; j < size; j++) {
- SuggestedWordInfo wordInfo = words.getWordInfo(j);
- mJsonWriter.value(wordInfo.toString());
- }
- mJsonWriter.endArray();
- mJsonWriter.endObject();
- } else if (value == null) {
- mJsonWriter.nullValue();
- } else {
- Log.w(TAG, "Unrecognized type to be logged: " +
- (value == null ? "<null>" : value.getClass().getName()));
- mJsonWriter.nullValue();
- }
- }
- mJsonWriter.endObject();
} catch (IOException e) {
e.printStackTrace();
Log.w(TAG, "Error in JsonWriter; disabling logging");
@@ -315,5 +221,6 @@ public class ResearchLog {
mJsonWriter = NULL_JSON_WRITER;
}
}
+ return mJsonWriter;
}
}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 9f65f5d3f..709746ee3 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -34,11 +34,11 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
-import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
+import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -84,7 +84,13 @@ import java.util.UUID;
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = ResearchLogger.class.getSimpleName();
private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
- private static final boolean LOG_EVERYTHING = false; // true will disclose private info
+ // Whether all n-grams should be logged. true will disclose private info.
+ private static final boolean LOG_EVERYTHING = false
+ && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
+ // Whether the TextView contents are logged at the end of the session. true will disclose
+ // private info.
+ private static final boolean LOG_FULL_TEXTVIEW_CONTENTS = false
+ && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
/* package */ static boolean sIsLogging = false;
private static final int OUTPUT_FORMAT_VERSION = 5;
@@ -94,8 +100,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final String FILENAME_SUFFIX = ".txt";
private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
+ // Whether to show an indicator on the screen that logging is on. Currently a very small red
+ // dot in the lower right hand corner. Most users should not notice it.
private static final boolean IS_SHOWING_INDICATOR = true;
- private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false;
+ // Change the default indicator to something very visible. Currently two red vertical bars on
+ // either side of they keyboard.
+ private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false || LOG_EVERYTHING;
public static final int FEEDBACK_WORD_BUFFER_SIZE = 5;
// constants related to specific log points
@@ -137,7 +147,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// used to check whether words are not unique
private Suggest mSuggest;
private MainKeyboardView mMainKeyboardView;
- private InputMethodService mInputMethodService;
+ private LatinIME mLatinIME;
private final Statistics mStatistics;
private Intent mUploadIntent;
@@ -152,16 +162,17 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return sInstance;
}
- public void init(final InputMethodService ims, final SharedPreferences prefs) {
- assert ims != null;
- if (ims == null) {
+ public void init(final LatinIME latinIME) {
+ assert latinIME != null;
+ if (latinIME == null) {
Log.w(TAG, "IMS is null; logging is off");
} else {
- mFilesDir = ims.getFilesDir();
+ mFilesDir = latinIME.getFilesDir();
if (mFilesDir == null || !mFilesDir.exists()) {
Log.w(TAG, "IME storage directory does not exist.");
}
}
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME);
if (prefs != null) {
mUUIDString = getUUID(prefs);
if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
@@ -182,12 +193,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
e.apply();
}
}
- mInputMethodService = ims;
+ mLatinIME = latinIME;
mPrefs = prefs;
- mUploadIntent = new Intent(mInputMethodService, UploaderService.class);
+ mUploadIntent = new Intent(mLatinIME, UploaderService.class);
if (ProductionFlag.IS_EXPERIMENTAL) {
- scheduleUploadingService(mInputMethodService);
+ scheduleUploadingService(mLatinIME);
}
}
@@ -246,7 +257,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (windowToken == null) {
return;
}
- final AlertDialog.Builder builder = new AlertDialog.Builder(mInputMethodService)
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mLatinIME)
.setTitle(R.string.research_splash_title)
.setMessage(R.string.research_splash_content)
.setPositiveButton(android.R.string.yes,
@@ -261,12 +272,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- final String packageName = mInputMethodService.getPackageName();
+ final String packageName = mLatinIME.getPackageName();
final Uri packageUri = Uri.parse("package:" + packageName);
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE,
packageUri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mInputMethodService.startActivity(intent);
+ mLatinIME.startActivity(intent);
}
})
.setCancelable(true)
@@ -274,7 +285,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
- mInputMethodService.requestHideSelf(0);
+ mLatinIME.requestHideSelf(0);
}
});
mSplashDialog = builder.create();
@@ -318,10 +329,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
private void checkForEmptyEditor() {
- if (mInputMethodService == null) {
+ if (mLatinIME == null) {
return;
}
- final InputConnection ic = mInputMethodService.getCurrentInputConnection();
+ final InputConnection ic = mLatinIME.getCurrentInputConnection();
if (ic == null) {
return;
}
@@ -374,7 +385,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (DEBUG) {
Log.d(TAG, "stop called");
}
- logStatistics();
commitCurrentLogUnit();
if (mMainLogBuffer != null) {
@@ -582,7 +592,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (DEBUG) {
Log.d(TAG, "calling uploadNow()");
}
- mInputMethodService.startService(mUploadIntent);
+ mLatinIME.startService(mUploadIntent);
}
public void onLeavingSendFeedbackDialog() {
@@ -643,7 +653,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final float savedStrokeWidth = paint.getStrokeWidth();
if (IS_SHOWING_INDICATOR_CLEARLY) {
paint.setStrokeWidth(5);
- canvas.drawRect(0, 0, width, height, paint);
+ canvas.drawLine(0, 0, 0, height, paint);
+ canvas.drawLine(width, 0, width, height, paint);
} else {
// Put a tiny red dot on the screen so a knowledgeable user can check whether
// it is enabled. The dot is actually a zero-width, zero-height rectangle,
@@ -681,7 +692,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (!mCurrentLogUnit.isEmpty()) {
if (mMainLogBuffer != null) {
mMainLogBuffer.shiftIn(mCurrentLogUnit);
- if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) {
+ if ((mMainLogBuffer.isSafeToLog() || LOG_EVERYTHING) && mMainResearchLog != null) {
publishLogBuffer(mMainLogBuffer, mMainResearchLog,
true /* isIncludingPrivateData */);
mMainLogBuffer.resetWordCounter();
@@ -701,6 +712,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
/* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
final ResearchLog researchLog, final boolean isIncludingPrivateData) {
final LogUnit openingLogUnit = new LogUnit();
+ if (logBuffer.isEmpty()) return;
openingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_OPENING, SystemClock.uptimeMillis(),
isIncludingPrivateData);
researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */);
@@ -731,7 +743,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final LogStatement LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS =
new LogStatement("recordSplitWords", true, false);
- private void onWordComplete(final String word, final long maxTime, final boolean isSplitWords) {
+ public void onWordComplete(final String word, final long maxTime) {
final Dictionary dictionary = getDictionary();
if (word != null && word.length() > 0 && hasLetters(word)) {
mCurrentLogUnit.setWord(word);
@@ -741,11 +753,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
enqueueCommitText(word);
- if (isSplitWords) {
- enqueueEvent(LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS);
- enqueueCommitText(" ");
- mStatistics.recordSplitWords();
- }
commitCurrentLogUnit();
mCurrentLogUnit = newLogUnit;
}
@@ -801,6 +808,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return WORD_REPLACEMENT_STRING;
}
+ // Specific logging methods follow below. The comments for each logging method should
+ // indicate what specific method is logged, and how to trigger it from the user interface.
+ //
+ // Logging methods can be generally classified into two flavors, "UserAction", which should
+ // correspond closely to an event that is sensed by the IME, and is usually generated
+ // directly by the user, and "SystemResponse" which corresponds to an event that the IME
+ // generates, often after much processing of user input. SystemResponses should correspond
+ // closely to user-visible events.
+ // TODO: Consider exposing the UserAction classification in the log output.
+
+ /**
+ * Log a call to LatinIME.onStartInputViewInternal().
+ *
+ * UserAction: called each time the keyboard is opened up.
+ */
private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL =
new LogStatement("LatinImeOnStartInputViewInternal", false, false, "uuid",
"packageName", "inputType", "imeOptions", "fieldId", "display", "model",
@@ -814,7 +836,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|| InputTypeUtils.isVisiblePasswordInputType(editorInfo.inputType);
getInstance().setIsPasswordView(isPassword);
researchLogger.start();
- final Context context = researchLogger.mInputMethodService;
+ final Context context = researchLogger.mLatinIME;
try {
final PackageInfo packageInfo;
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
@@ -835,9 +857,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
public void latinIME_onFinishInputViewInternal() {
+ logStatistics();
stop();
}
+ /**
+ * Log a change in preferences.
+ *
+ * UserAction: called when the user changes the settings.
+ */
private static final LogStatement LOGSTATEMENT_PREFS_CHANGED =
new LogStatement("PrefsChanged", false, false, "prefs");
public static void prefsChanged(final SharedPreferences prefs) {
@@ -845,8 +873,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
researchLogger.enqueueEvent(LOGSTATEMENT_PREFS_CHANGED, prefs);
}
- // Regular logging methods
-
+ /**
+ * Log a call to MainKeyboardView.processMotionEvent().
+ *
+ * UserAction: called when the user puts their finger onto the screen (ACTION_DOWN).
+ *
+ */
private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
new LogStatement("MainKeyboardViewProcessMotionEvent", true, false, "action",
"eventTime", "id", "x", "y", "size", "pressure");
@@ -871,6 +903,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to LatinIME.onCodeInput().
+ *
+ * SystemResponse: The main processing step for entering text. Called when the user performs a
+ * tap, a flick, a long press, releases a gesture, or taps a punctuation suggestion.
+ */
private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT =
new LogStatement("LatinImeOnCodeInput", true, false, "code", "x", "y");
public static void latinIME_onCodeInput(final int code, final int x, final int y) {
@@ -883,6 +921,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
researchLogger.mStatistics.recordChar(code, time);
}
+ /**
+ * Log a call to LatinIME.onDisplayCompletions().
+ *
+ * SystemResponse: The IME has displayed application-specific completions. They may show up
+ * in the suggestion strip, such as a landscape phone.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS =
new LogStatement("LatinIMEOnDisplayCompletions", true, true,
"applicationSpecifiedCompletions");
@@ -900,6 +944,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return returnValue;
}
+ /**
+ * Log a call to LatinIME.onWindowHidden().
+ *
+ * UserAction: The user has performed an action that has caused the IME to be closed. They may
+ * have focused on something other than a text field, or explicitly closed it.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN =
new LogStatement("LatinIMEOnWindowHidden", false, false, "isTextTruncated", "text");
public static void latinIME_onWindowHidden(final int savedSelectionStart,
@@ -907,7 +957,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (ic != null) {
final boolean isTextTruncated;
final String text;
- if (LOG_EVERYTHING) {
+ if (LOG_FULL_TEXTVIEW_CONTENTS) {
// 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
@@ -955,6 +1005,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to LatinIME.onUpdateSelection().
+ *
+ * UserAction/SystemResponse: The user has moved the cursor or selection. This function may
+ * be called, however, when the system has moved the cursor, say by inserting a character.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_ONUPDATESELECTION =
new LogStatement("LatinIMEOnUpdateSelection", true, false, "lastSelectionStart",
"lastSelectionEnd", "oldSelStart", "oldSelEnd", "newSelStart", "newSelEnd",
@@ -981,27 +1037,48 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
expectingUpdateSelectionFromLogger, scrubbedWord);
}
+ /**
+ * Log a call to LatinIME.pickSuggestionManually().
+ *
+ * UserAction: The user has chosen a specific word from the suggestion strip.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY =
new LogStatement("LatinIMEPickSuggestionManually", true, false, "replacedWord", "index",
"suggestion", "x", "y");
public static void latinIME_pickSuggestionManually(final String replacedWord,
- final int index, CharSequence suggestion) {
+ final int index, final String suggestion) {
+ final String scrubbedWord = scrubDigitsFromString(suggestion);
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY,
scrubDigitsFromString(replacedWord), index,
- suggestion == null ? null : scrubDigitsFromString(suggestion.toString()),
- Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+ suggestion == null ? null : scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE,
+ Constants.SUGGESTION_STRIP_COORDINATE);
+ researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE);
+ researchLogger.mStatistics.recordManualSuggestion();
}
+ /**
+ * Log a call to LatinIME.punctuationSuggestion().
+ *
+ * UserAction: The user has chosen punctuation from the punctuation suggestion strip.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION =
new LogStatement("LatinIMEPunctuationSuggestion", false, false, "index", "suggestion",
"x", "y");
- public static void latinIME_punctuationSuggestion(final int index,
- final CharSequence suggestion) {
- getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
+ public static void latinIME_punctuationSuggestion(final int index, final String suggestion) {
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+ researchLogger.onWordComplete(suggestion, Long.MAX_VALUE);
}
+ /**
+ * Log a call to LatinIME.sendKeyCodePoint().
+ *
+ * SystemResponse: The IME is simulating a hardware keypress. This happens for numbers; other
+ * input typically goes through RichInputConnection.setComposingText() and
+ * RichInputConnection.commitText().
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT =
new LogStatement("LatinIMESendKeyCodePoint", true, false, "code");
public static void latinIME_sendKeyCodePoint(final int code) {
@@ -1013,18 +1090,36 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to LatinIME.swapSwapperAndSpace().
+ *
+ * SystemResponse: A symbol has been swapped with a space character. E.g. punctuation may swap
+ * if a soft space is inserted after a word.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE =
new LogStatement("LatinIMESwapSwapperAndSpace", false, false);
public static void latinIME_swapSwapperAndSpace() {
getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE);
}
+ /**
+ * Log a call to MainKeyboardView.onLongPress().
+ *
+ * UserAction: The user has performed a long-press on a key.
+ */
private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS =
new LogStatement("MainKeyboardViewOnLongPress", false, false);
public static void mainKeyboardView_onLongPress() {
getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS);
}
+ /**
+ * Log a call to MainKeyboardView.setKeyboard().
+ *
+ * SystemResponse: The IME has switched to a new keyboard (e.g. French, English).
+ * This is typically called right after LatinIME.onStartInputViewInternal (when starting a new
+ * IME), but may happen at other times if the user explicitly requests a keyboard change.
+ */
private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD =
new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale",
"orientation", "width", "modeName", "action", "navigateNext",
@@ -1045,18 +1140,43 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
keyboard.mOccupiedHeight, keyboard.mKeys);
}
+ /**
+ * Log a call to LatinIME.revertCommit().
+ *
+ * SystemResponse: The IME has reverted commited text. This happens when the user enters
+ * a word, commits it by pressing space or punctuation, and then reverts the commit by hitting
+ * backspace.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_REVERTCOMMIT =
- new LogStatement("LatinIMERevertCommit", true, false, "originallyTypedWord");
- public static void latinIME_revertCommit(final String originallyTypedWord) {
- getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, originallyTypedWord);
+ new LogStatement("LatinIMERevertCommit", true, false, "committedWord",
+ "originallyTypedWord");
+ public static void latinIME_revertCommit(final String committedWord,
+ final String originallyTypedWord) {
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord,
+ originallyTypedWord);
+ researchLogger.mStatistics.recordRevertCommit();
}
+ /**
+ * Log a call to PointerTracker.callListenerOnCancelInput().
+ *
+ * UserAction: The user has canceled the input, e.g., by pressing down, but then removing
+ * outside the keyboard area.
+ * TODO: Verify
+ */
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT =
new LogStatement("PointerTrackerCallListenerOnCancelInput", false, false);
public static void pointerTracker_callListenerOnCancelInput() {
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT);
}
+ /**
+ * Log a call to PointerTracker.callListenerOnCodeInput().
+ *
+ * SystemResponse: The user has entered a key through the normal tapping mechanism.
+ * LatinIME.onCodeInput will also be called.
+ */
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT =
new LogStatement("PointerTrackerCallListenerOnCodeInput", true, false, "code",
"outputText", "x", "y", "ignoreModifierKey", "altersCode", "isEnabled");
@@ -1072,6 +1192,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to PointerTracker.callListenerCallListenerOnRelease().
+ *
+ * UserAction: The user has released their finger or thumb from the screen.
+ */
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE =
new LogStatement("PointerTrackerCallListenerOnRelease", true, false, "code",
"withSliding", "ignoreModifierKey", "isEnabled");
@@ -1084,6 +1209,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to PointerTracker.onDownEvent().
+ *
+ * UserAction: The user has pressed down on a key.
+ * TODO: Differentiate with LatinIME.processMotionEvent.
+ */
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT =
new LogStatement("PointerTrackerOnDownEvent", true, false, "deltaT", "distanceSquared");
public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
@@ -1091,6 +1222,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
distanceSquared);
}
+ /**
+ * Log a call to PointerTracker.onMoveEvent().
+ *
+ * UserAction: The user has moved their finger while pressing on the screen.
+ * TODO: Differentiate with LatinIME.processMotionEvent().
+ */
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT =
new LogStatement("PointerTrackerOnMoveEvent", true, false, "x", "y", "lastX", "lastY");
public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
@@ -1098,6 +1235,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT, x, y, lastX, lastY);
}
+ /**
+ * Log a call to RichInputConnection.commitCompletion().
+ *
+ * SystemResponse: The IME has committed a completion. A completion is an application-
+ * specific suggestion that is presented in a pop-up menu in the TextView.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION =
new LogStatement("RichInputConnectionCommitCompletion", true, false, "completionInfo");
public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) {
@@ -1106,34 +1249,79 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
completionInfo);
}
+ /**
+ * Log a call to LatinIME.commitCurrentAutoCorrection().
+ *
+ * SystemResponse: The IME has committed an auto-correction. An auto-correction changes the raw
+ * text input to another word that the user more likely desired to type.
+ */
+ private static final LogStatement LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION =
+ new LogStatement("LatinIMECommitCurrentAutoCorrection", true, false, "typedWord",
+ "autoCorrection", "separatorString");
+ public static void latinIme_commitCurrentAutoCorrection(final String typedWord,
+ final String autoCorrection, final String separatorString) {
+ final String scrubbedTypedWord = scrubDigitsFromString(typedWord);
+ final String scrubbedAutoCorrection = scrubDigitsFromString(autoCorrection);
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION,
+ scrubbedTypedWord, scrubbedAutoCorrection, separatorString);
+ researchLogger.onWordComplete(scrubbedAutoCorrection, Long.MAX_VALUE);
+ }
+
private boolean isExpectingCommitText = false;
+ /**
+ * Log a call to RichInputConnection.commitPartialText
+ *
+ * SystemResponse: The IME is committing part of a word. This happens if a space is
+ * automatically inserted to split a single typed string into two or more words.
+ */
+ // TODO: This method is currently unused. Find where it should be called from in the IME and
+ // add invocations.
+ private static final LogStatement LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT =
+ new LogStatement("LatinIMECommitPartialText", true, false, "newCursorPosition");
public static void latinIME_commitPartialText(final CharSequence committedWord,
final long lastTimestampOfWordData) {
final ResearchLogger researchLogger = getInstance();
final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
- researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData, true /* isPartial */);
- researchLogger.isExpectingCommitText = true;
+ researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT);
+ researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData);
+ researchLogger.mStatistics.recordSplitWords();
}
- private static final LogStatement LOGSTATEMENT_COMMITTEXT_UPDATECURSOR =
- new LogStatement("CommitTextUpdateCursor", true, false, "newCursorPosition");
+ /**
+ * Log a call to RichInputConnection.commitText().
+ *
+ * SystemResponse: The IME is committing text. This happens after the user has typed a word
+ * and then a space or punctuation key.
+ */
+ private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT =
+ new LogStatement("RichInputConnectionCommitText", true, false, "newCursorPosition");
public static void richInputConnection_commitText(final CharSequence committedWord,
final int newCursorPosition) {
final ResearchLogger researchLogger = getInstance();
final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
if (!researchLogger.isExpectingCommitText) {
- researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE, false /* isPartial */);
- researchLogger.enqueueEvent(LOGSTATEMENT_COMMITTEXT_UPDATECURSOR, newCursorPosition);
+ researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT,
+ newCursorPosition);
+ researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE);
}
researchLogger.isExpectingCommitText = false;
}
+ /**
+ * Shared event for logging committed text.
+ */
private static final LogStatement LOGSTATEMENT_COMMITTEXT =
new LogStatement("CommitText", true, false, "committedText");
private void enqueueCommitText(final CharSequence word) {
enqueueEvent(LOGSTATEMENT_COMMITTEXT, word);
}
+ /**
+ * Log a call to RichInputConnection.deleteSurroundingText().
+ *
+ * SystemResponse: The IME has deleted text.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT =
new LogStatement("RichInputConnectionDeleteSurroundingText", true, false,
"beforeLength", "afterLength");
@@ -1143,20 +1331,37 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
beforeLength, afterLength);
}
+ /**
+ * Log a call to RichInputConnection.finishComposingText().
+ *
+ * SystemResponse: The IME has left the composing text as-is.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT =
new LogStatement("RichInputConnectionFinishComposingText", false, false);
public static void richInputConnection_finishComposingText() {
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT);
}
+ /**
+ * Log a call to RichInputConnection.performEditorAction().
+ *
+ * SystemResponse: The IME is invoking an action specific to the editor.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION =
new LogStatement("RichInputConnectionPerformEditorAction", false, false,
- "imeActionNext");
- public static void richInputConnection_performEditorAction(final int imeActionNext) {
+ "imeActionId");
+ public static void richInputConnection_performEditorAction(final int imeActionId) {
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION,
- imeActionNext);
+ imeActionId);
}
+ /**
+ * Log a call to RichInputConnection.sendKeyEvent().
+ *
+ * SystemResponse: The IME is telling the TextView that a key is being pressed through an
+ * alternate channel.
+ * TODO: only for hardware keys?
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT =
new LogStatement("RichInputConnectionSendKeyEvent", true, false, "eventTime", "action",
"code");
@@ -1165,6 +1370,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
keyEvent.getEventTime(), keyEvent.getAction(), keyEvent.getKeyCode());
}
+ /**
+ * Log a call to RichInputConnection.setComposingText().
+ *
+ * SystemResponse: The IME is setting the composing text. Happens each time a character is
+ * entered.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT =
new LogStatement("RichInputConnectionSetComposingText", true, true, "text",
"newCursorPosition");
@@ -1177,12 +1388,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
newCursorPosition);
}
+ /**
+ * Log a call to RichInputConnection.setSelection().
+ *
+ * SystemResponse: The IME is requesting that the selection change. User-initiated selection-
+ * change requests do not go through this method -- it's only when the system wants to change
+ * the selection.
+ */
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION =
new LogStatement("RichInputConnectionSetSelection", true, false, "from", "to");
public static void richInputConnection_setSelection(final int from, final int to) {
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION, from, to);
}
+ /**
+ * Log a call to SuddenJumpingTouchEventHandler.onTouchEvent().
+ *
+ * SystemResponse: The IME has filtered input events in case of an erroneous sensor reading.
+ */
private static final LogStatement LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT =
new LogStatement("SuddenJumpingTouchEventHandlerOnTouchEvent", true, false,
"motionEvent");
@@ -1193,6 +1416,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * Log a call to SuggestionsView.setSuggestions().
+ *
+ * SystemResponse: The IME is setting the suggestions in the suggestion strip.
+ */
private static final LogStatement LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS =
new LogStatement("SuggestionStripViewSetSuggestions", true, true, "suggestedWords");
public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
@@ -1202,12 +1430,22 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ /**
+ * The user has indicated a particular point in the log that is of interest.
+ *
+ * UserAction: From direct menu invocation.
+ */
private static final LogStatement LOGSTATEMENT_USER_TIMESTAMP =
new LogStatement("UserTimestamp", false, false);
public void userTimestamp() {
getInstance().enqueueEvent(LOGSTATEMENT_USER_TIMESTAMP);
}
+ /**
+ * Log a call to LatinIME.onEndBatchInput().
+ *
+ * SystemResponse: The system has completed a gesture.
+ */
private static final LogStatement LOGSTATEMENT_LATINIME_ONENDBATCHINPUT =
new LogStatement("LatinIMEOnEndBatchInput", true, false, "enteredText",
"enteredWordPos");
@@ -1216,15 +1454,35 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText,
enteredWordPos);
- researchLogger.mStatistics.recordGestureInput();
+ researchLogger.mStatistics.recordGestureInput(enteredText.length());
}
+ /**
+ * Log a call to LatinIME.handleBackspace().
+ *
+ * UserInput: The user is deleting a gestured word by hitting the backspace key once.
+ */
+ private static final LogStatement LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH =
+ new LogStatement("LatinIMEHandleBackspaceBatch", true, false, "deletedText");
+ public static void latinIME_handleBackspace_batch(final CharSequence deletedText) {
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH, deletedText);
+ researchLogger.mStatistics.recordGestureDelete();
+ }
+
+ /**
+ * Log statistics.
+ *
+ * ContextualData, recorded at the end of a session.
+ */
private static final LogStatement LOGSTATEMENT_STATISTICS =
new LogStatement("Statistics", false, false, "charCount", "letterCount", "numberCount",
"spaceCount", "deleteOpsCount", "wordCount", "isEmptyUponStarting",
"isEmptinessStateKnown", "averageTimeBetweenKeys", "averageTimeBeforeDelete",
"averageTimeDuringRepeatedDelete", "averageTimeAfterDelete",
- "dictionaryWordCount", "splitWordsCount", "gestureInputCount");
+ "dictionaryWordCount", "splitWordsCount", "gestureInputCount",
+ "gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount",
+ "revertCommitsCount");
private static void logStatistics() {
final ResearchLogger researchLogger = getInstance();
final Statistics statistics = researchLogger.mStatistics;
@@ -1236,6 +1494,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
statistics.mAfterDeleteKeyCounter.getAverageTime(),
statistics.mDictionaryWordCount, statistics.mSplitWordsCount,
- statistics.mGestureInputCount);
+ statistics.mGesturesInputCount, statistics.mGesturesCharsCount,
+ statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount,
+ statistics.mRevertCommitsCount);
}
}
diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java
index 23d1050cb..e9c02c919 100644
--- a/java/src/com/android/inputmethod/research/Statistics.java
+++ b/java/src/com/android/inputmethod/research/Statistics.java
@@ -42,7 +42,15 @@ public class Statistics {
// Number of words split and spaces automatically entered.
int mSplitWordsCount;
// Number of gestures that were input.
- int mGestureInputCount;
+ int mGesturesInputCount;
+ // Number of gestures that were deleted.
+ int mGesturesDeletedCount;
+ // Total number of characters in words entered by gesture.
+ int mGesturesCharsCount;
+ // Number of manual suggestions chosen.
+ int mManualSuggestionsCount;
+ // Number of times a commit was reverted in this session.
+ int mRevertCommitsCount;
// Whether the text field was empty upon editing
boolean mIsEmptyUponStarting;
boolean mIsEmptinessStateKnown;
@@ -103,12 +111,20 @@ public class Statistics {
mSpaceCount = 0;
mDeleteKeyCount = 0;
mWordCount = 0;
+ mDictionaryWordCount = 0;
+ mSplitWordsCount = 0;
+ mGesturesInputCount = 0;
+ mGesturesDeletedCount = 0;
+ mManualSuggestionsCount = 0;
+ mRevertCommitsCount = 0;
mIsEmptyUponStarting = true;
mIsEmptinessStateKnown = false;
mKeyCounter.reset();
mBeforeDeleteKeyCounter.reset();
mDuringRepeatedDeleteKeysCounter.reset();
mAfterDeleteKeyCounter.reset();
+ mGesturesCharsCount = 0;
+ mGesturesDeletedCount = 0;
mLastTapTime = 0;
mIsLastKeyDeleteKey = false;
@@ -161,12 +177,24 @@ public class Statistics {
mSplitWordsCount++;
}
- public void recordGestureInput() {
- mGestureInputCount++;
+ public void recordGestureInput(final int numCharsEntered) {
+ mGesturesInputCount++;
+ mGesturesCharsCount += numCharsEntered;
}
public void setIsEmptyUponStarting(final boolean isEmpty) {
mIsEmptyUponStarting = isEmpty;
mIsEmptinessStateKnown = true;
}
+
+ public void recordGestureDelete() {
+ mGesturesDeletedCount++;
+ }
+ public void recordManualSuggestion() {
+ mManualSuggestionsCount++;
+ }
+
+ public void recordRevertCommit() {
+ mRevertCommitsCount++;
+ }
}
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
index 7a5749096..a1ecc1118 100644
--- a/java/src/com/android/inputmethod/research/UploaderService.java
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -30,6 +30,7 @@ import android.os.Bundle;
import android.util.Log;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.define.ProductionFlag;
import java.io.BufferedReader;
import java.io.File;
@@ -45,6 +46,10 @@ import java.net.URL;
public final class UploaderService extends IntentService {
private static final String TAG = UploaderService.class.getSimpleName();
+ private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
+ // Set IS_INHIBITING_AUTO_UPLOAD to true for local testing
+ private static final boolean IS_INHIBITING_AUTO_UPLOAD = false
+ && ProductionFlag.IS_EXPERIMENTAL_DEBUG; // Force false in production
public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR;
private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName()
+ ".extra.UPLOAD_UNCONDITIONALLY";
@@ -116,7 +121,8 @@ public final class UploaderService extends IntentService {
}
private void doUpload(final boolean isUploadingUnconditionally) {
- if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) {
+ if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection()
+ || IS_INHIBITING_AUTO_UPLOAD)) {
return;
}
if (mFilesDir == null) {
@@ -141,7 +147,9 @@ public final class UploaderService extends IntentService {
}
private boolean uploadFile(File file) {
- Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
+ if (DEBUG) {
+ Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
+ }
boolean success = false;
final int contentLength = (int) file.length();
HttpURLConnection connection = null;
@@ -157,6 +165,9 @@ public final class UploaderService extends IntentService {
int numBytesRead;
while ((numBytesRead = fileInputStream.read(buf)) != -1) {
os.write(buf, 0, numBytesRead);
+ if (DEBUG) {
+ Log.d(TAG, new String(buf));
+ }
}
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "upload failed: " + connection.getResponseCode());
@@ -171,7 +182,9 @@ public final class UploaderService extends IntentService {
}
file.delete();
success = true;
- Log.d(TAG, "upload successful");
+ if (DEBUG) {
+ Log.d(TAG, "upload successful");
+ }
} catch (Exception e) {
e.printStackTrace();
} finally {