LatinIME/java/src/com/android/inputmethod/research/Statistics.java
Tadashi G. Takaoka 72c2feb573 Use TimeUnit instead of DateUtils for readability
Bug: 9550800
Change-Id: I087205530a5dbcff4bf08f48f4aa7068aae93215
2013-07-05 17:57:01 +09:00

279 lines
9.6 KiB
Java

/*
* 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.util.Log;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.define.ProductionFlag;
import java.util.concurrent.TimeUnit;
public class Statistics {
private static final String TAG = Statistics.class.getSimpleName();
private static final boolean DEBUG = false
&& ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
// TODO: Cleanup comments to only including those giving meaningful information.
// Number of characters entered during a typing session
int mCharCount;
// Number of letter characters entered during a typing session
int mLetterCount;
// Number of number characters entered
int mNumberCount;
// Number of space characters entered
int mSpaceCount;
// Number of delete operations entered (taps on the backspace key)
int mDeleteKeyCount;
// Number of words entered during a session.
int mWordCount;
// Number of words found in the dictionary.
int mDictionaryWordCount;
// Number of words split and spaces automatically entered.
int mSplitWordsCount;
// Number of words entered during a session.
int mCorrectedWordsCount;
// Number of gestures that were input.
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 that autocorrection was invoked.
int mAutoCorrectionsCount;
// Number of times a commit was reverted in this session.
int mRevertCommitsCount;
// Whether the text field was empty upon editing
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();
final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter();
final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter();
final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter();
static class AverageTimeCounter {
int mCount;
int mTotalTime;
public void reset() {
mCount = 0;
mTotalTime = 0;
}
public void add(long deltaTime) {
mCount++;
mTotalTime += deltaTime;
}
public int getAverageTime() {
if (mCount == 0) {
return 0;
}
return mTotalTime / mCount;
}
}
// To account for the interruptions when the user's attention is directed elsewhere, times
// longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic.
public static final long MIN_TYPING_INTERMISSION = TimeUnit.SECONDS.toMillis(2);
public static final long MIN_DELETION_INTERMISSION = TimeUnit.SECONDS.toMillis(10);
// The last time that a tap was performed
private long mLastTapTime;
// The type of the last keypress (delete key or not)
boolean mIsLastKeyDeleteKey;
private static final Statistics sInstance = new Statistics();
public static Statistics getInstance() {
return sInstance;
}
private Statistics() {
reset();
}
public void reset() {
mCharCount = 0;
mLetterCount = 0;
mNumberCount = 0;
mSpaceCount = 0;
mDeleteKeyCount = 0;
mWordCount = 0;
mDictionaryWordCount = 0;
mSplitWordsCount = 0;
mCorrectedWordsCount = 0;
mGesturesInputCount = 0;
mGesturesDeletedCount = 0;
mManualSuggestionsCount = 0;
mRevertCommitsCount = 0;
mAutoCorrectionsCount = 0;
mIsEmptyUponStarting = true;
mIsEmptinessStateKnown = false;
mKeyCounter.reset();
mBeforeDeleteKeyCounter.reset();
mDuringRepeatedDeleteKeysCounter.reset();
mAfterDeleteKeyCounter.reset();
mGesturesCharsCount = 0;
mGesturesDeletedCount = 0;
mPublishableCount = 0;
mUnpublishableStoppingCount = 0;
mUnpublishableIncorrectWordCount = 0;
mUnpublishableSampledTooRecently = 0;
mUnpublishableDictionaryUnavailable = 0;
mUnpublishableMayContainDigit = 0;
mUnpublishableNotInDictionary = 0;
mLastTapTime = 0;
mIsLastKeyDeleteKey = false;
}
public void recordChar(int codePoint, long time) {
if (DEBUG) {
Log.d(TAG, "recordChar() called");
}
if (codePoint == Constants.CODE_DELETE) {
mDeleteKeyCount++;
recordUserAction(time, true /* isDeletion */);
} else {
mCharCount++;
if (Character.isDigit(codePoint)) {
mNumberCount++;
}
if (Character.isLetter(codePoint)) {
mLetterCount++;
}
if (Character.isSpaceChar(codePoint)) {
mSpaceCount++;
}
recordUserAction(time, false /* isDeletion */);
}
}
public void recordWordEntered(final boolean isDictionaryWord,
final boolean containsCorrection) {
mWordCount++;
if (isDictionaryWord) {
mDictionaryWordCount++;
}
if (containsCorrection) {
mCorrectedWordsCount++;
}
}
public void recordSplitWords() {
mSplitWordsCount++;
}
public void recordGestureInput(final int numCharsEntered, final long time) {
mGesturesInputCount++;
mGesturesCharsCount += numCharsEntered;
recordUserAction(time, false /* isDeletion */);
}
public void setIsEmptyUponStarting(final boolean isEmpty) {
mIsEmptyUponStarting = isEmpty;
mIsEmptinessStateKnown = true;
}
public void recordGestureDelete(final int length, final long time) {
mGesturesDeletedCount++;
recordUserAction(time, true /* isDeletion */);
}
public void recordManualSuggestion(final long time) {
mManualSuggestionsCount++;
recordUserAction(time, false /* isDeletion */);
}
public void recordAutoCorrection(final long time) {
mAutoCorrectionsCount++;
recordUserAction(time, false /* isDeletion */);
}
public void recordRevertCommit(final long time) {
mRevertCommitsCount++;
recordUserAction(time, true /* isDeletion */);
}
private void recordUserAction(final long time, final boolean isDeletion) {
final long delta = time - mLastTapTime;
if (isDeletion) {
if (delta < MIN_DELETION_INTERMISSION) {
if (mIsLastKeyDeleteKey) {
mDuringRepeatedDeleteKeysCounter.add(delta);
} else {
mBeforeDeleteKeyCounter.add(delta);
}
} else {
ResearchLogger.onUserPause(delta);
}
} else {
if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) {
mAfterDeleteKeyCounter.add(delta);
} else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) {
mKeyCounter.add(delta);
} else {
ResearchLogger.onUserPause(delta);
}
}
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;
}
}
}