diff options
Diffstat (limited to 'java/src/com/android/inputmethod/research')
7 files changed, 260 insertions, 73 deletions
diff --git a/java/src/com/android/inputmethod/research/FeedbackLog.java b/java/src/com/android/inputmethod/research/FeedbackLog.java new file mode 100644 index 000000000..5af194c32 --- /dev/null +++ b/java/src/com/android/inputmethod/research/FeedbackLog.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 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.Context; + +import java.io.File; + +public class FeedbackLog extends ResearchLog { + public FeedbackLog(final File outputFile, final Context context) { + super(outputFile, context); + } + + @Override + public boolean isFeedbackLog() { + return true; + } +} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index cf1388f46..164c7e8cc 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -67,7 +67,7 @@ public class LogUnit { private String[] mWordArray = EMPTY_STRING_ARRAY; private boolean mMayContainDigit; private boolean mIsPartOfMegaword; - private boolean mContainsCorrection; + private boolean mContainsUserDeletions; // mCorrectionType indicates whether the word was corrected at all, and if so, the nature of the // correction. @@ -277,13 +277,13 @@ public class LogUnit { } // TODO: Refactor to eliminate getter/setters - public void setContainsCorrection() { - mContainsCorrection = true; + public void setContainsUserDeletions() { + mContainsUserDeletions = true; } // TODO: Refactor to eliminate getter/setters - public boolean containsCorrection() { - return mContainsCorrection; + public boolean containsUserDeletions() { + return mContainsUserDeletions; } // TODO: Refactor to eliminate getter/setters @@ -323,7 +323,7 @@ public class LogUnit { true /* isPartOfMegaword */); newLogUnit.mWords = null; newLogUnit.mMayContainDigit = mMayContainDigit; - newLogUnit.mContainsCorrection = mContainsCorrection; + newLogUnit.mContainsUserDeletions = mContainsUserDeletions; // Purge the logStatements and associated data from this LogUnit. laterLogStatements.clear(); @@ -346,7 +346,7 @@ public class LogUnit { setWords(logUnit.mWords); } mMayContainDigit = mMayContainDigit || logUnit.mMayContainDigit; - mContainsCorrection = mContainsCorrection || logUnit.mContainsCorrection; + mContainsUserDeletions = mContainsUserDeletions || logUnit.mContainsUserDeletions; mIsPartOfMegaword = false; } diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java index 9aa349906..3482153b4 100644 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -63,6 +63,15 @@ public abstract class MainLogBuffer extends FixedLogBuffer { private static final boolean DEBUG = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; + // Keep consistent with switch statement in Statistics.recordPublishabilityResultCode() + public static final int PUBLISHABILITY_PUBLISHABLE = 0; + public static final int PUBLISHABILITY_UNPUBLISHABLE_STOPPING = 1; + public static final int PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT = 2; + public static final int PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY = 3; + public static final int PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE = 4; + public static final int PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT = 5; + public static final int PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY = 6; + // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. public static final int N_GRAM_SIZE = 2; @@ -105,21 +114,24 @@ public abstract class MainLogBuffer extends FixedLogBuffer { } /** - * Determines whether uploading the n words at the front the MainLogBuffer will not violate - * user privacy. + * Determines whether the string determined by a series of LogUnits will not violate user + * privacy if published. + * + * @param logUnits a LogUnit list to check for publishability + * @param nGramSize the smallest n-gram acceptable to be published. if + * {@link ResearchLogger.IS_LOGGING_EVERYTHING} is true, then publish if there are more than + * {@code minNGramSize} words in the logUnits, otherwise wait. if {@link + * ResearchLogger.IS_LOGGING_EVERYTHING} is false, then ensure that there are exactly nGramSize + * words in the LogUnits. * - * 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. + * @return one of the {@code PUBLISHABILITY_*} result codes defined in this class. */ - private boolean isSafeNGram(final ArrayList<LogUnit> logUnits, final int minNGramSize) { + private int getPublishabilityResultCode(final ArrayList<LogUnit> logUnits, + final int nGramSize) { // Bypass privacy checks when debugging. if (ResearchLogger.IS_LOGGING_EVERYTHING) { if (mIsStopping) { - return true; + return PUBLISHABILITY_UNPUBLISHABLE_STOPPING; } // Only check that it is the right length. If not, wait for later words to make // complete n-grams. @@ -129,13 +141,17 @@ public abstract class MainLogBuffer extends FixedLogBuffer { final LogUnit logUnit = logUnits.get(i); numWordsInLogUnitList += logUnit.getNumWords(); } - return numWordsInLogUnitList >= minNGramSize; + if (numWordsInLogUnitList >= nGramSize) { + return PUBLISHABILITY_PUBLISHABLE; + } else { + return PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT; + } } // Check that we are not sampling too frequently. Having sampled recently might disclose // too much of the user's intended meaning. if (mNumWordsUntilSafeToSample > 0) { - return false; + return PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY; } // Reload the dictionary in case it has changed (e.g., because the user has changed // languages). @@ -144,7 +160,7 @@ public abstract class MainLogBuffer extends FixedLogBuffer { // 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; + return PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE; } // Check each word in the buffer. If any word poses a privacy threat, we cannot upload @@ -155,7 +171,7 @@ public abstract class MainLogBuffer extends FixedLogBuffer { if (!logUnit.hasOneOrMoreWords()) { // Digits outside words are a privacy threat. if (logUnit.mayContainDigit()) { - return false; + return PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT; } } else { numWordsInLogUnitList += logUnit.getNumWords(); @@ -168,14 +184,18 @@ public abstract class MainLogBuffer extends FixedLogBuffer { + ResearchLogger.hasLetters(word) + ", isValid: " + (dictionary.isValidWord(word))); } - return false; + return PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY; } } } } // Finally, only return true if the ngram is the right size. - return numWordsInLogUnitList == minNGramSize; + if (numWordsInLogUnitList == nGramSize) { + return PUBLISHABILITY_PUBLISHABLE; + } else { + return PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT; + } } public void shiftAndPublishAll() throws IOException { @@ -196,11 +216,29 @@ public abstract class MainLogBuffer extends FixedLogBuffer { } } + /** + * If there is a safe n-gram at the front of this log buffer, publish it with all details, and + * remove the LogUnits that constitute it. + * + * An n-gram might not be "safe" if it violates privacy controls. E.g., it might contain + * numbers, an out-of-vocabulary word, or another n-gram may have been published recently. If + * there is no safe n-gram, then the LogUnits up through the first word-containing LogUnit are + * published, but without disclosing any privacy-related details, such as the word the LogUnit + * generated, motion data, etc. + * + * Note that a LogUnit can hold more than one word if the user types without explicit spaces. + * In this case, the words may be grouped together in such a way that pulling an n-gram off the + * front would require splitting a LogUnit. Splitting a LogUnit is not possible, so this case + * is treated just as the unsafe n-gram case. This may cause n-grams to be sampled at slightly + * less than the target frequency. + */ protected final void publishLogUnitsAtFrontOfBuffer() throws IOException { // TODO: Refactor this method to require fewer passes through the LogUnits. Should really // require only one pass. ArrayList<LogUnit> logUnits = peekAtFirstNWords(N_GRAM_SIZE); - if (isSafeNGram(logUnits, N_GRAM_SIZE)) { + final int publishabilityResultCode = getPublishabilityResultCode(logUnits, N_GRAM_SIZE); + ResearchLogger.recordPublishabilityResultCode(publishabilityResultCode); + if (publishabilityResultCode == MainLogBuffer.PUBLISHABILITY_PUBLISHABLE) { // Good n-gram at the front of the buffer. Publish it, disclosing details. publish(logUnits, true /* canIncludePrivateData */); shiftOutWords(N_GRAM_SIZE); diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 3e82139a6..fde2798e1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -81,6 +81,17 @@ public class ResearchLog { } /** + * Returns true if this is a FeedbackLog. + * + * FeedbackLogs record only the data associated with a Feedback dialog. Instead of normal + * logging, they contain a LogStatement with the complete feedback string and optionally a + * recording of the user's supplied demo of the problem. + */ + public boolean isFeedbackLog() { + return false; + } + + /** * Waits for any publication requests to finish and closes the {@link JsonWriter} used for * output. * diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 8b8ea21e9..56ab90cb4 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -194,10 +194,17 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // gesture, and when committing the earlier word, split the LogUnit. private long mSavedDownEventTime; private Bundle mFeedbackDialogBundle = null; + // Whether the feedback dialog is visible, and the user is typing into it. Normal logging is + // not performed on text that the user types into the feedback dialog. private boolean mInFeedbackDialog = false; private Handler mUserRecordingTimeoutHandler; private static final long USER_RECORDING_TIMEOUT_MS = 30L * DateUtils.SECOND_IN_MILLIS; + // Stores a temporary LogUnit while generating a phantom space. Needed because phantom spaces + // are issued out-of-order, immediately before the characters generated by other operations that + // have already outputted LogStatements. + private LogUnit mPhantomSpaceLogUnit = null; + private ResearchLogger() { mStatistics = Statistics.getInstance(); } @@ -253,14 +260,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (DEBUG) { final String wordsString = logUnit.getWordsAsString(); Log.d(TAG, "onPublish: '" + wordsString - + "', hc: " + logUnit.containsCorrection() + + "', hc: " + logUnit.containsUserDeletions() + ", cipd: " + canIncludePrivateData); } for (final String word : logUnit.getWordsAsStringArray()) { final Dictionary dictionary = getDictionary(); mStatistics.recordWordEntered( dictionary != null && dictionary.isValidWord(word), - logUnit.containsCorrection()); + logUnit.containsUserDeletions()); } } publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData); @@ -650,7 +657,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(), feedbackContents, accountName, recording); - final ResearchLog feedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( + final ResearchLog feedbackLog = new FeedbackLog(mResearchLogDirectory.getLogFilePath( System.currentTimeMillis(), System.nanoTime()), mLatinIME); final LogBuffer feedbackLogBuffer = new LogBuffer(); feedbackLogBuffer.shiftIn(feedbackLogUnit); @@ -713,8 +720,28 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mIsPasswordView = isPasswordView; } - private boolean isAllowedToLog() { - return !mIsPasswordView && sIsLogging && !mInFeedbackDialog; + /** + * Returns true if logging is permitted. + * + * This method is called when adding a LogStatement to a LogUnit, and when adding a LogUnit to a + * ResearchLog. It is checked in both places in case conditions change between these times, and + * as a defensive measure in case refactoring changes the logging pipeline. + */ + private boolean isAllowedToLogTo(final ResearchLog researchLog) { + // Logging is never allowed in these circumstances + if (mIsPasswordView) return false; + if (!sIsLogging) return false; + if (mInFeedbackDialog) { + // The FeedbackDialog is up. Normal logging should not happen (the user might be trying + // out things while the dialog is up, and their reporting of an issue may not be + // representative of what they normally type). However, after the user has finished + // entering their feedback, the logger packs their comments and an encoded version of + // any demonstration of the issue into a special "FeedbackLog". So if the FeedbackLog + // is the destination, we do want to allow logging to it. + return researchLog.isFeedbackLog(); + } + // No other exclusions. Logging is permitted. + return true; } public void requestIndicatorRedraw() { @@ -747,7 +774,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // and remove this method. // The check for MainKeyboardView ensures that the indicator only decorates the main // keyboard, not every keyboard. - if (IS_SHOWING_INDICATOR && (isAllowedToLog() || isReplaying()) + if (IS_SHOWING_INDICATOR && (isAllowedToLogTo(mMainResearchLog) || isReplaying()) && view instanceof MainKeyboardView) { final int savedColor = paint.getColor(); paint.setColor(getIndicatorColor()); @@ -782,7 +809,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private synchronized void enqueueEvent(final LogUnit logUnit, final LogStatement logStatement, final Object... values) { assert values.length == logStatement.getKeys().length; - if (isAllowedToLog() && logUnit != null) { + if (isAllowedToLogTo(mMainResearchLog) && logUnit != null) { final long time = SystemClock.uptimeMillis(); logUnit.addLogStatement(logStatement, time, values); } @@ -792,8 +819,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mCurrentLogUnit.setMayContainDigit(); } - private void setCurrentLogUnitContainsCorrection() { - mCurrentLogUnit.setContainsCorrection(); + private void setCurrentLogUnitContainsUserDeletions() { + mCurrentLogUnit.setContainsUserDeletions(); } private void setCurrentLogUnitCorrectionType(final int correctionType) { @@ -881,7 +908,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLog researchLog, final boolean canIncludePrivateData) { final LogUnit openingLogUnit = new LogUnit(); if (logUnits.isEmpty()) return; - if (!isAllowedToLog()) return; + if (!isAllowedToLogTo(researchLog)) return; // LogUnits not containing private data, such as contextual data for the log, do not require // logSegment boundary statements. if (canIncludePrivateData) { @@ -893,7 +920,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (DEBUG) { Log.d(TAG, "publishLogBuffer: " + (logUnit.hasOneOrMoreWords() ? logUnit.getWordsAsString() : "<wordless>") - + ", correction?: " + logUnit.containsCorrection()); + + ", correction?: " + logUnit.containsUserDeletions()); } researchLog.publish(logUnit, canIncludePrivateData); } @@ -1259,7 +1286,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final ResearchLogger researchLogger = getInstance(); if (!replacedWord.equals(suggestion.toString())) { // The user chose something other than what was already there. - researchLogger.setCurrentLogUnitContainsCorrection(); + researchLogger.setCurrentLogUnitContainsUserDeletions(); researchLogger.setCurrentLogUnitCorrectionType(LogUnit.CORRECTIONTYPE_TYPO); } final String scrubbedWord = scrubDigitsFromString(suggestion); @@ -1291,17 +1318,32 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang /** * Log a call to LatinIME.sendKeyCodePoint(). * - * SystemResponse: The IME is inserting text into the TextView for numbers, fixed strings, or - * some other unusual mechanism. + * SystemResponse: The IME is inserting text into the TextView for non-word-constituent, + * strings (separators, numbers, other symbols). */ private static final LogStatement LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT = new LogStatement("LatinIMESendKeyCodePoint", true, false, "code"); public static void latinIME_sendKeyCodePoint(final int code) { final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT, - Constants.printableCode(scrubDigitFromCodePoint(code))); - if (Character.isDigit(code)) { - researchLogger.setCurrentLogUnitContainsDigitFlag(); + final LogUnit phantomSpaceLogUnit = researchLogger.mPhantomSpaceLogUnit; + if (phantomSpaceLogUnit == null) { + researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT, + Constants.printableCode(scrubDigitFromCodePoint(code))); + if (Character.isDigit(code)) { + researchLogger.setCurrentLogUnitContainsDigitFlag(); + } + researchLogger.commitCurrentLogUnit(); + } else { + researchLogger.enqueueEvent(phantomSpaceLogUnit, LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT, + Constants.printableCode(scrubDigitFromCodePoint(code))); + if (Character.isDigit(code)) { + phantomSpaceLogUnit.setMayContainDigit(); + } + researchLogger.mMainLogBuffer.shiftIn(phantomSpaceLogUnit); + if (researchLogger.mUserRecordingLogBuffer != null) { + researchLogger.mUserRecordingLogBuffer.shiftIn(phantomSpaceLogUnit); + } + researchLogger.mPhantomSpaceLogUnit = null; } } @@ -1311,12 +1353,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang * SystemResponse: The IME is inserting a real space in place of a phantom space. */ private static final LogStatement LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE = - new LogStatement("LatinIMEPromotPhantomSpace", false, false); + new LogStatement("LatinIMEPromotePhantomSpace", false, false); public static void latinIME_promotePhantomSpace() { + // A phantom space is always added before the text that triggered it. The triggering text + // and the events that created it will be in mCurrentLogUnit, but the phantom space should + // be in its own LogUnit, committed before the triggering text. Although it is created + // here, it is not added to the LogBuffer until the following call to + // latinIME_sendKeyCodePoint, because SENDKEYCODEPOINT LogStatement also must go into that + // LogUnit. final ResearchLogger researchLogger = getInstance(); - final LogUnit logUnit; - logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE); + researchLogger.mPhantomSpaceLogUnit = new LogUnit(); + researchLogger.enqueueEvent(researchLogger.mPhantomSpaceLogUnit, + LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE); } /** @@ -1415,10 +1463,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord, originallyTypedWord, separatorString); if (logUnit != null) { - logUnit.setContainsCorrection(); + logUnit.setContainsUserDeletions(); } researchLogger.mStatistics.recordRevertCommit(SystemClock.uptimeMillis()); - researchLogger.commitCurrentLogUnitAsWord(originallyTypedWord, Long.MAX_VALUE, isBatchMode); } /** @@ -1571,25 +1618,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private boolean isExpectingCommitText = false; - /** - * Log a call to (UnknownClass).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_COMMIT_PARTIAL_TEXT = - new LogStatement("CommitPartialText", true, false, "newCursorPosition"); - public static void commitPartialText(final String committedWord, - final long lastTimestampOfWordData, final boolean isBatchMode) { - final ResearchLogger researchLogger = getInstance(); - final String scrubbedWord = scrubDigitsFromString(committedWord); - researchLogger.enqueueEvent(LOGSTATEMENT_COMMIT_PARTIAL_TEXT); - researchLogger.mStatistics.recordAutoCorrection(SystemClock.uptimeMillis()); - researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData, - isBatchMode); - } /** * Log a call to RichInputConnection.commitText(). @@ -1613,12 +1641,24 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /** - * Shared event for logging committed text. + * Shared events for logging committed text. + * + * The "CommitTextEventHappened" LogStatement is written to the log even if privacy rules + * indicate that the word contents should not be logged. It has no contents, and only serves to + * record the event and thereby make it easier to calculate word-level statistics even when the + * word contents are unknown. */ private static final LogStatement LOGSTATEMENT_COMMITTEXT = - new LogStatement("CommitText", true, false, "committedText", "isBatchMode"); + new LogStatement("CommitText", true /* isPotentiallyPrivate */, + false /* isPotentiallyRevealing */, "committedText", "isBatchMode"); + private static final LogStatement LOGSTATEMENT_COMMITTEXT_EVENT_HAPPENED = + new LogStatement("CommitTextEventHappened", false /* isPotentiallyPrivate */, + false /* isPotentiallyRevealing */); private void enqueueCommitText(final String word, final boolean isBatchMode) { + // Event containing the word; will be published only if privacy checks pass enqueueEvent(LOGSTATEMENT_COMMITTEXT, word, isBatchMode); + // Event not containing the word; will always be published + enqueueEvent(LOGSTATEMENT_COMMITTEXT_EVENT_HAPPENED); } /** @@ -1837,6 +1877,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } /** + * Call this method when the logging system has attempted publication of an n-gram. + * + * Statistics are gathered about the success or failure. + * + * @param publishabilityResultCode a result code as defined by + * {@code MainLogBuffer.PUBLISHABILITY_*} + */ + static void recordPublishabilityResultCode(final int publishabilityResultCode) { + final ResearchLogger researchLogger = getInstance(); + final Statistics statistics = researchLogger.mStatistics; + statistics.recordPublishabilityResultCode(publishabilityResultCode); + } + + /** * Log statistics. * * ContextualData, recorded at the end of a session. @@ -1848,7 +1902,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete", "dictionaryWordCount", "splitWordsCount", "gestureInputCount", "gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount", - "revertCommitsCount", "correctedWordsCount", "autoCorrectionsCount"); + "revertCommitsCount", "correctedWordsCount", "autoCorrectionsCount", + "publishableCount", "unpublishableStoppingCount", + "unpublishableIncorrectWordCount", "unpublishableSampledTooRecentlyCount", + "unpublishableDictionaryUnavailableCount", "unpublishableMayContainDigitCount", + "unpublishableNotInDictionaryCount"); private static void logStatistics() { final ResearchLogger researchLogger = getInstance(); final Statistics statistics = researchLogger.mStatistics; @@ -1863,6 +1921,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang statistics.mGesturesInputCount, statistics.mGesturesCharsCount, statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount, statistics.mRevertCommitsCount, statistics.mCorrectedWordsCount, - statistics.mAutoCorrectionsCount); + statistics.mAutoCorrectionsCount, statistics.mPublishableCount, + statistics.mUnpublishableStoppingCount, statistics.mUnpublishableIncorrectWordCount, + statistics.mUnpublishableSampledTooRecently, + statistics.mUnpublishableDictionaryUnavailable, + statistics.mUnpublishableMayContainDigit, statistics.mUnpublishableNotInDictionary); } } diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java index 7f6c851bb..e573ca012 100644 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ b/java/src/com/android/inputmethod/research/Statistics.java @@ -61,6 +61,16 @@ public class Statistics { boolean mIsEmptyUponStarting; boolean mIsEmptinessStateKnown; + // Counts of how often an n-gram is collected or not, and the reasons for the decision. + // Keep consistent with publishability result code list in MainLogBuffer + int mPublishableCount; + int mUnpublishableStoppingCount; + int mUnpublishableIncorrectWordCount; + int mUnpublishableSampledTooRecently; + int mUnpublishableDictionaryUnavailable; + int mUnpublishableMayContainDigit; + int mUnpublishableNotInDictionary; + // 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(); @@ -133,6 +143,13 @@ public class Statistics { mAfterDeleteKeyCounter.reset(); mGesturesCharsCount = 0; mGesturesDeletedCount = 0; + mPublishableCount = 0; + mUnpublishableStoppingCount = 0; + mUnpublishableIncorrectWordCount = 0; + mUnpublishableSampledTooRecently = 0; + mUnpublishableDictionaryUnavailable = 0; + mUnpublishableMayContainDigit = 0; + mUnpublishableNotInDictionary = 0; mLastTapTime = 0; mIsLastKeyDeleteKey = false; @@ -230,4 +247,31 @@ public class Statistics { mIsLastKeyDeleteKey = isDeletion; mLastTapTime = time; } + + public void recordPublishabilityResultCode(final int publishabilityResultCode) { + // Keep consistent with publishability result code list in MainLogBuffer + switch (publishabilityResultCode) { + case MainLogBuffer.PUBLISHABILITY_PUBLISHABLE: + mPublishableCount++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_STOPPING: + mUnpublishableStoppingCount++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT: + mUnpublishableIncorrectWordCount++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY: + mUnpublishableSampledTooRecently++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE: + mUnpublishableDictionaryUnavailable++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT: + mUnpublishableMayContainDigit++; + break; + case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY: + mUnpublishableNotInDictionary++; + break; + } + } } diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java index ba05ec12b..c7ea3e69d 100644 --- a/java/src/com/android/inputmethod/research/Uploader.java +++ b/java/src/com/android/inputmethod/research/Uploader.java @@ -49,7 +49,7 @@ public final class Uploader { private static final boolean DEBUG = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; // Set IS_INHIBITING_AUTO_UPLOAD to true for local testing - private static final boolean IS_INHIBITING_AUTO_UPLOAD = false + private static final boolean IS_INHIBITING_UPLOAD = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; private static final int BUF_SIZE = 1024 * 8; @@ -76,7 +76,7 @@ public final class Uploader { } public boolean isPossibleToUpload() { - return hasUploadingPermission() && mUrl != null && !IS_INHIBITING_AUTO_UPLOAD; + return hasUploadingPermission() && mUrl != null && !IS_INHIBITING_UPLOAD; } private boolean hasUploadingPermission() { |