diff options
Diffstat (limited to 'java/src/com/android/inputmethod/research')
5 files changed, 112 insertions, 37 deletions
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 884ade06b..27c4027de 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -24,10 +24,12 @@ 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; @@ -95,6 +97,22 @@ import java.util.Map; */ 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++) { @@ -110,6 +128,23 @@ import java.util.Map; 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); + } } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b02278d1a..ec3d4eda4 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.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; @@ -84,7 +83,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 +99,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 @@ -136,9 +145,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // used to check whether words are not unique private Suggest mSuggest; - private Dictionary mDictionary; private MainKeyboardView mMainKeyboardView; - private InputMethodService mInputMethodService; + private LatinIME mLatinIME; private final Statistics mStatistics; private Intent mUploadIntent; @@ -153,12 +161,12 @@ 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, final SharedPreferences prefs) { + 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."); } @@ -183,12 +191,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); } } @@ -247,7 +255,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, @@ -262,12 +270,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) @@ -275,7 +283,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - mInputMethodService.requestHideSelf(0); + mLatinIME.requestHideSelf(0); } }); mSplashDialog = builder.create(); @@ -319,10 +327,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; } @@ -375,7 +383,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (DEBUG) { Log.d(TAG, "stop called"); } - logStatistics(); commitCurrentLogUnit(); if (mMainLogBuffer != null) { @@ -583,7 +590,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (DEBUG) { Log.d(TAG, "calling uploadNow()"); } - mInputMethodService.startService(mUploadIntent); + mLatinIME.startService(mUploadIntent); } public void onLeavingSendFeedbackDialog() { @@ -597,6 +604,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } + private Dictionary getDictionary() { + if (mSuggest == null) { + return null; + } + return mSuggest.getMainDictionary(); + } + private void setIsPasswordView(boolean isPasswordView) { mIsPasswordView = isPasswordView; } @@ -637,7 +651,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, @@ -695,6 +710,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 */); @@ -726,10 +742,11 @@ 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) { + final Dictionary dictionary = getDictionary(); if (word != null && word.length() > 0 && hasLetters(word)) { mCurrentLogUnit.setWord(word); - final boolean isDictionaryWord = mDictionary != null - && mDictionary.isValidWord(word); + final boolean isDictionaryWord = dictionary != null + && dictionary.isValidWord(word); mStatistics.recordWordEntered(isDictionaryWord); } final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime); @@ -784,10 +801,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private String scrubWord(String word) { - if (mDictionary == null) { + final Dictionary dictionary = getDictionary(); + if (dictionary == null) { return WORD_REPLACEMENT_STRING; } - if (mDictionary.isValidWord(word)) { + if (dictionary.isValidWord(word)) { return word; } return WORD_REPLACEMENT_STRING; @@ -821,7 +839,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(), @@ -842,6 +860,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } public void latinIME_onFinishInputViewInternal() { + logStatistics(); stop(); } @@ -941,7 +960,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 @@ -1458,8 +1477,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), statistics.mAfterDeleteKeyCounter.getAverageTime(), statistics.mDictionaryWordCount, statistics.mSplitWordsCount, - statistics.mGestureInputCount, - statistics.mGestureCharsCount, + statistics.mGesturesInputCount, + statistics.mGesturesCharsCount, statistics.mGesturesDeletedCount); } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index f9c072967..a02aabf3c 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -42,11 +42,11 @@ 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 mGestureCharsCount; + int mGesturesCharsCount; // Whether the text field was empty upon editing boolean mIsEmptyUponStarting; boolean mIsEmptinessStateKnown; @@ -107,13 +107,17 @@ public class Statistics { mSpaceCount = 0; mDeleteKeyCount = 0; mWordCount = 0; + mDictionaryWordCount = 0; + mSplitWordsCount = 0; + mGesturesInputCount = 0; + mGesturesDeletedCount = 0; mIsEmptyUponStarting = true; mIsEmptinessStateKnown = false; mKeyCounter.reset(); mBeforeDeleteKeyCounter.reset(); mDuringRepeatedDeleteKeysCounter.reset(); mAfterDeleteKeyCounter.reset(); - mGestureCharsCount = 0; + mGesturesCharsCount = 0; mGesturesDeletedCount = 0; mLastTapTime = 0; @@ -168,8 +172,8 @@ public class Statistics { } public void recordGestureInput(final int numCharsEntered) { - mGestureInputCount++; - mGestureCharsCount += numCharsEntered; + mGesturesInputCount++; + mGesturesCharsCount += numCharsEntered; } public void setIsEmptyUponStarting(final boolean isEmpty) { 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 { |