ResearchLogger: make logging more reliable (esp on startup)
Bug: 6188932 Change-Id: I692e427ba2e6da7bb15f48208304c4a034392a22main
parent
9370ab9ada
commit
0df487678e
|
@ -223,15 +223,15 @@
|
||||||
<string name="notify_recorded_timestamp">Recorded timestamp</string>
|
<string name="notify_recorded_timestamp">Recorded timestamp</string>
|
||||||
|
|
||||||
<!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] -->
|
<!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] -->
|
||||||
<string name="do_not_log_this_session">Do not log this session</string>
|
<string name="do_not_log_this_session">Suspend logging</string>
|
||||||
<!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
|
<!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
|
||||||
<string name="enable_session_logging">Enable session logging</string>
|
<string name="enable_session_logging">Enable logging</string>
|
||||||
<!-- Title for dialog option to let users log all events in this session [CHAR LIMIT=35] -->
|
<!-- Title for dialog option to let users log all events in this session [CHAR LIMIT=35] -->
|
||||||
<string name="log_whole_session_history">Log whole session history</string>
|
<string name="log_whole_session_history">Log whole session history</string>
|
||||||
<!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] -->
|
<!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] -->
|
||||||
<string name="notify_session_log_deleting">Deleting session log</string>
|
<string name="notify_session_log_deleting">Deleting session log</string>
|
||||||
<!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] -->
|
<!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] -->
|
||||||
<string name="notify_session_log_deleted">Session log deleted</string>
|
<string name="notify_logging_suspended">Logging temporarily suspended. To disable permanently, go to Android Keyboard Settings</string>
|
||||||
<!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] -->
|
<!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] -->
|
||||||
<string name="notify_session_log_not_deleted">Session log NOT deleted</string>
|
<string name="notify_session_log_not_deleted">Session log NOT deleted</string>
|
||||||
<!-- Toast notification that the system has recorded the whole session history [CHAR LIMIT=35] -->
|
<!-- Toast notification that the system has recorded the whole session history [CHAR LIMIT=35] -->
|
||||||
|
@ -240,7 +240,6 @@
|
||||||
<string name="notify_session_history_not_logged">Error: Session history NOT logged</string>
|
<string name="notify_session_history_not_logged">Error: Session history NOT logged</string>
|
||||||
<!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] -->
|
<!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] -->
|
||||||
<string name="notify_session_logging_enabled">Session logging enabled</string>
|
<string name="notify_session_logging_enabled">Session logging enabled</string>
|
||||||
|
|
||||||
<!-- Preference for input language selection -->
|
<!-- Preference for input language selection -->
|
||||||
<string name="select_language">Input languages</string>
|
<string name="select_language">Input languages</string>
|
||||||
|
|
||||||
|
|
|
@ -618,7 +618,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
+ ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
|
+ ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
|
||||||
}
|
}
|
||||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||||
ResearchLogger.getInstance().start();
|
|
||||||
ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
|
ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
|
||||||
}
|
}
|
||||||
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
|
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
|
||||||
|
@ -711,7 +710,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
|
|
||||||
LatinImeLogger.commit();
|
LatinImeLogger.commit();
|
||||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||||
ResearchLogger.getInstance().stop();
|
ResearchLogger.getInstance().latinIME_onFinishInputInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
|
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
|
||||||
|
|
|
@ -55,13 +55,14 @@ public class ResearchLog {
|
||||||
|
|
||||||
final ScheduledExecutorService mExecutor;
|
final ScheduledExecutorService mExecutor;
|
||||||
/* package */ final File mFile;
|
/* package */ final File mFile;
|
||||||
private JsonWriter mJsonWriter = NULL_JSON_WRITER; // should never be null
|
private JsonWriter mJsonWriter = NULL_JSON_WRITER;
|
||||||
|
|
||||||
private int mLoggingState;
|
private int mLoggingState;
|
||||||
private static final int LOGGING_STATE_UNSTARTED = 0;
|
private static final int LOGGING_STATE_UNSTARTED = 0;
|
||||||
private static final int LOGGING_STATE_RUNNING = 1;
|
private static final int LOGGING_STATE_READY = 1; // don't create file until necessary
|
||||||
private static final int LOGGING_STATE_STOPPING = 2;
|
private static final int LOGGING_STATE_RUNNING = 2;
|
||||||
private static final int LOGGING_STATE_STOPPED = 3;
|
private static final int LOGGING_STATE_STOPPING = 3;
|
||||||
|
private static final int LOGGING_STATE_STOPPED = 4;
|
||||||
private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
|
private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
|
||||||
|
|
||||||
private static class NullOutputStream extends OutputStream {
|
private static class NullOutputStream extends OutputStream {
|
||||||
|
@ -94,11 +95,9 @@ public class ResearchLog {
|
||||||
public synchronized void start() throws IOException {
|
public synchronized void start() throws IOException {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
|
mLoggingState = LOGGING_STATE_READY;
|
||||||
mJsonWriter.setLenient(true);
|
|
||||||
mJsonWriter.beginArray();
|
|
||||||
mLoggingState = LOGGING_STATE_RUNNING;
|
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
case LOGGING_STATE_STOPPING:
|
case LOGGING_STATE_STOPPING:
|
||||||
case LOGGING_STATE_STOPPED:
|
case LOGGING_STATE_STOPPED:
|
||||||
|
@ -111,6 +110,7 @@ public class ResearchLog {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
mLoggingState = LOGGING_STATE_STOPPED;
|
mLoggingState = LOGGING_STATE_STOPPED;
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
mExecutor.submit(new Callable<Object>() {
|
mExecutor.submit(new Callable<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,14 +120,13 @@ public class ResearchLog {
|
||||||
mJsonWriter.flush();
|
mJsonWriter.flush();
|
||||||
mJsonWriter.close();
|
mJsonWriter.close();
|
||||||
} finally {
|
} finally {
|
||||||
// the contentprovider only exports data if the writable
|
|
||||||
// bit is cleared.
|
|
||||||
boolean success = mFile.setWritable(false, false);
|
boolean success = mFile.setWritable(false, false);
|
||||||
mLoggingState = LOGGING_STATE_STOPPED;
|
mLoggingState = LOGGING_STATE_STOPPED;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
removeAnyScheduledFlush();
|
||||||
mExecutor.shutdown();
|
mExecutor.shutdown();
|
||||||
mLoggingState = LOGGING_STATE_STOPPING;
|
mLoggingState = LOGGING_STATE_STOPPING;
|
||||||
break;
|
break;
|
||||||
|
@ -139,27 +138,26 @@ public class ResearchLog {
|
||||||
public boolean isAlive() {
|
public boolean isAlive() {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitUntilStopped(int timeoutInMs) throws InterruptedException {
|
public void waitUntilStopped(final int timeoutInMs) throws InterruptedException {
|
||||||
|
removeAnyScheduledFlush();
|
||||||
|
mExecutor.shutdown();
|
||||||
mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS);
|
mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAbortSuccessful;
|
|
||||||
public boolean isAbortSuccessful() {
|
|
||||||
return isAbortSuccessful;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void abort() {
|
public synchronized void abort() {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
mLoggingState = LOGGING_STATE_STOPPED;
|
mLoggingState = LOGGING_STATE_STOPPED;
|
||||||
isAbortSuccessful = true;
|
isAbortSuccessful = true;
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
mExecutor.submit(new Callable<Object>() {
|
mExecutor.submit(new Callable<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -173,6 +171,7 @@ public class ResearchLog {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
removeAnyScheduledFlush();
|
||||||
mExecutor.shutdown();
|
mExecutor.shutdown();
|
||||||
mLoggingState = LOGGING_STATE_STOPPING;
|
mLoggingState = LOGGING_STATE_STOPPING;
|
||||||
break;
|
break;
|
||||||
|
@ -181,10 +180,16 @@ public class ResearchLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAbortSuccessful;
|
||||||
|
public boolean isAbortSuccessful() {
|
||||||
|
return isAbortSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
/* package */ synchronized void flush() {
|
/* package */ synchronized void flush() {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
removeAnyScheduledFlush();
|
removeAnyScheduledFlush();
|
||||||
mExecutor.submit(mFlushCallable);
|
mExecutor.submit(mFlushCallable);
|
||||||
|
@ -197,7 +202,9 @@ public class ResearchLog {
|
||||||
private Callable<Object> mFlushCallable = new Callable<Object>() {
|
private Callable<Object> mFlushCallable = new Callable<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object call() throws Exception {
|
public Object call() throws Exception {
|
||||||
mJsonWriter.flush();
|
if (mLoggingState == LOGGING_STATE_RUNNING) {
|
||||||
|
mJsonWriter.flush();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -220,6 +227,7 @@ public class ResearchLog {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
mExecutor.submit(new Callable<Object>() {
|
mExecutor.submit(new Callable<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -239,6 +247,7 @@ public class ResearchLog {
|
||||||
switch (mLoggingState) {
|
switch (mLoggingState) {
|
||||||
case LOGGING_STATE_UNSTARTED:
|
case LOGGING_STATE_UNSTARTED:
|
||||||
break;
|
break;
|
||||||
|
case LOGGING_STATE_READY:
|
||||||
case LOGGING_STATE_RUNNING:
|
case LOGGING_STATE_RUNNING:
|
||||||
mExecutor.submit(new Callable<Object>() {
|
mExecutor.submit(new Callable<Object>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -260,6 +269,11 @@ public class ResearchLog {
|
||||||
void outputEvent(final String[] keys, final Object[] values) {
|
void outputEvent(final String[] keys, final Object[] values) {
|
||||||
// not thread safe.
|
// not thread safe.
|
||||||
try {
|
try {
|
||||||
|
if (mJsonWriter == NULL_JSON_WRITER) {
|
||||||
|
mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
|
||||||
|
mJsonWriter.setLenient(true);
|
||||||
|
mJsonWriter.beginArray();
|
||||||
|
}
|
||||||
mJsonWriter.beginObject();
|
mJsonWriter.beginObject();
|
||||||
mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
|
mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
|
||||||
mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis());
|
mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis());
|
||||||
|
|
|
@ -44,7 +44,6 @@ import com.android.inputmethod.latin.RichInputConnection.Range;
|
||||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -64,13 +63,15 @@ import java.util.UUID;
|
||||||
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
private static final String TAG = ResearchLogger.class.getSimpleName();
|
private static final String TAG = ResearchLogger.class.getSimpleName();
|
||||||
private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
|
private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
|
||||||
|
/* package */ static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
|
||||||
/* package */ static boolean sIsLogging = false;
|
/* package */ static boolean sIsLogging = false;
|
||||||
private static final int OUTPUT_FORMAT_VERSION = 1;
|
private static final int OUTPUT_FORMAT_VERSION = 1;
|
||||||
private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
|
private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
|
||||||
private static final String FILENAME_PREFIX = "researchLog";
|
/* package */ static final String FILENAME_PREFIX = "researchLog";
|
||||||
private static final String FILENAME_SUFFIX = ".txt";
|
private static final String FILENAME_SUFFIX = ".txt";
|
||||||
private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
|
private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
|
||||||
new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
|
new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
|
||||||
|
private static final boolean IS_SHOWING_INDICATOR = false;
|
||||||
|
|
||||||
// constants related to specific log points
|
// constants related to specific log points
|
||||||
private static final String WHITESPACE_SEPARATORS = " \t\n\r";
|
private static final String WHITESPACE_SEPARATORS = " \t\n\r";
|
||||||
|
@ -92,6 +93,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
|
|
||||||
private boolean mIsPasswordView = false;
|
private boolean mIsPasswordView = false;
|
||||||
private boolean mIsLoggingSuspended = false;
|
private boolean mIsLoggingSuspended = false;
|
||||||
|
private SharedPreferences mPrefs;
|
||||||
|
|
||||||
// digits entered by the user are replaced with this codepoint.
|
// digits entered by the user are replaced with this codepoint.
|
||||||
/* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
|
/* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
|
||||||
|
@ -101,6 +103,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
|
private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
|
||||||
private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
|
private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
|
||||||
private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
|
private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
|
||||||
|
protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
|
||||||
// set when LatinIME should ignore an onUpdateSelection() callback that
|
// set when LatinIME should ignore an onUpdateSelection() callback that
|
||||||
// arises from operations in this class
|
// arises from operations in this class
|
||||||
private static boolean sLatinIMEExpectingUpdateSelection = false;
|
private static boolean sLatinIMEExpectingUpdateSelection = false;
|
||||||
|
@ -124,7 +127,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (ims == null) {
|
if (ims == null) {
|
||||||
Log.w(TAG, "IMS is null; logging is off");
|
Log.w(TAG, "IMS is null; logging is off");
|
||||||
} else {
|
} else {
|
||||||
mContext = ims;
|
|
||||||
mFilesDir = ims.getFilesDir();
|
mFilesDir = ims.getFilesDir();
|
||||||
if (mFilesDir == null || !mFilesDir.exists()) {
|
if (mFilesDir == null || !mFilesDir.exists()) {
|
||||||
Log.w(TAG, "IME storage directory does not exist.");
|
Log.w(TAG, "IME storage directory does not exist.");
|
||||||
|
@ -132,6 +134,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
mUUIDString = getUUID(prefs);
|
mUUIDString = getUUID(prefs);
|
||||||
|
if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
|
||||||
|
Editor e = prefs.edit();
|
||||||
|
e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE);
|
||||||
|
e.apply();
|
||||||
|
}
|
||||||
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
@ -146,6 +153,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mKeyboardSwitcher = keyboardSwitcher;
|
mKeyboardSwitcher = keyboardSwitcher;
|
||||||
|
mContext = ims;
|
||||||
|
mPrefs = prefs;
|
||||||
|
|
||||||
|
// TODO: force user to decide at splash screen instead of defaulting to on.
|
||||||
|
setLoggingAllowed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupLoggingDir(final File dir, final long time) {
|
private void cleanupLoggingDir(final File dir, final long time) {
|
||||||
|
@ -166,8 +178,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
return new File(filesDir, sb.toString());
|
return new File(filesDir, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
private void start() {
|
||||||
if (!sIsLogging) {
|
updateSuspendedState();
|
||||||
|
requestIndicatorRedraw();
|
||||||
|
if (!isAllowedToLog()) {
|
||||||
// Log.w(TAG, "not in usability mode; not logging");
|
// Log.w(TAG, "not in usability mode; not logging");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -175,10 +189,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
|
Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
|
|
||||||
mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
|
if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
|
||||||
|
mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
||||||
|
}
|
||||||
mMainResearchLog.start();
|
mMainResearchLog.start();
|
||||||
if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) {
|
if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) {
|
||||||
mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
||||||
|
@ -189,15 +203,26 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
/* package */ void stop() {
|
||||||
if (mMainResearchLog != null) {
|
if (mMainResearchLog != null) {
|
||||||
mMainResearchLog.stop();
|
mMainResearchLog.stop();
|
||||||
}
|
}
|
||||||
|
if (mIntentionalResearchLog != null) {
|
||||||
|
mIntentionalResearchLog.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLoggingAllowed(boolean enableLogging) {
|
||||||
|
if (mPrefs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Editor e = mPrefs.edit();
|
||||||
|
e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
|
||||||
|
e.apply();
|
||||||
|
sIsLogging = enableLogging;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean abort() {
|
public boolean abort() {
|
||||||
mIsLoggingSuspended = true;
|
|
||||||
requestIndicatorRedraw();
|
|
||||||
boolean didAbortMainLog = false;
|
boolean didAbortMainLog = false;
|
||||||
if (mMainResearchLog != null) {
|
if (mMainResearchLog != null) {
|
||||||
mMainResearchLog.abort();
|
mMainResearchLog.abort();
|
||||||
|
@ -209,6 +234,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (mMainResearchLog.isAbortSuccessful()) {
|
if (mMainResearchLog.isAbortSuccessful()) {
|
||||||
didAbortMainLog = true;
|
didAbortMainLog = true;
|
||||||
}
|
}
|
||||||
|
mMainResearchLog = null;
|
||||||
}
|
}
|
||||||
boolean didAbortIntentionalLog = false;
|
boolean didAbortIntentionalLog = false;
|
||||||
if (mIntentionalResearchLog != null) {
|
if (mIntentionalResearchLog != null) {
|
||||||
|
@ -221,6 +247,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (mIntentionalResearchLog.isAbortSuccessful()) {
|
if (mIntentionalResearchLog.isAbortSuccessful()) {
|
||||||
didAbortIntentionalLog = true;
|
didAbortIntentionalLog = true;
|
||||||
}
|
}
|
||||||
|
mIntentionalResearchLog = null;
|
||||||
}
|
}
|
||||||
return didAbortMainLog && didAbortIntentionalLog;
|
return didAbortMainLog && didAbortIntentionalLog;
|
||||||
}
|
}
|
||||||
|
@ -247,6 +274,34 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void restart() {
|
||||||
|
stop();
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long mResumeTime = 0L;
|
||||||
|
private void suspendLoggingUntil(long time) {
|
||||||
|
mIsLoggingSuspended = true;
|
||||||
|
mResumeTime = time;
|
||||||
|
requestIndicatorRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resumeLogging() {
|
||||||
|
mResumeTime = 0L;
|
||||||
|
updateSuspendedState();
|
||||||
|
requestIndicatorRedraw();
|
||||||
|
if (isAllowedToLog()) {
|
||||||
|
restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSuspendedState() {
|
||||||
|
final long time = System.currentTimeMillis();
|
||||||
|
if (time > mResumeTime) {
|
||||||
|
mIsLoggingSuspended = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||||
if (key == null || prefs == null) {
|
if (key == null || prefs == null) {
|
||||||
|
@ -256,13 +311,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (sIsLogging == false) {
|
if (sIsLogging == false) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
requestIndicatorRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void presentResearchDialog(final LatinIME latinIME) {
|
/* package */ void presentResearchDialog(final LatinIME latinIME) {
|
||||||
final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
|
final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
|
||||||
|
final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
|
||||||
final CharSequence[] items = new CharSequence[] {
|
final CharSequence[] items = new CharSequence[] {
|
||||||
latinIME.getString(R.string.note_timestamp_for_researchlog),
|
latinIME.getString(R.string.note_timestamp_for_researchlog),
|
||||||
mIsLoggingSuspended ? latinIME.getString(R.string.enable_session_logging) :
|
showEnable ? latinIME.getString(R.string.enable_session_logging) :
|
||||||
latinIME.getString(R.string.do_not_log_this_session),
|
latinIME.getString(R.string.do_not_log_this_session),
|
||||||
latinIME.getString(R.string.log_whole_session_history),
|
latinIME.getString(R.string.log_whole_session_history),
|
||||||
};
|
};
|
||||||
|
@ -277,25 +334,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
Toast.LENGTH_LONG).show();
|
Toast.LENGTH_LONG).show();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (mIsLoggingSuspended) {
|
if (showEnable) {
|
||||||
mIsLoggingSuspended = false;
|
if (!sIsLogging) {
|
||||||
requestIndicatorRedraw();
|
setLoggingAllowed(true);
|
||||||
Toast toast = Toast.makeText(latinIME,
|
}
|
||||||
R.string.notify_session_logging_enabled, Toast.LENGTH_LONG);
|
resumeLogging();
|
||||||
|
Toast.makeText(latinIME, R.string.notify_session_logging_enabled,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
Toast toast = Toast.makeText(latinIME,
|
Toast toast = Toast.makeText(latinIME,
|
||||||
R.string.notify_session_log_deleting, Toast.LENGTH_LONG);
|
R.string.notify_session_log_deleting, Toast.LENGTH_LONG);
|
||||||
toast.show();
|
toast.show();
|
||||||
boolean isLogDeleted = abort();
|
boolean isLogDeleted = abort();
|
||||||
|
final long currentTime = System.currentTimeMillis();
|
||||||
|
final long resumeTime = currentTime + 1000 * 60 *
|
||||||
|
SUSPEND_DURATION_IN_MINUTES;
|
||||||
|
suspendLoggingUntil(resumeTime);
|
||||||
toast.cancel();
|
toast.cancel();
|
||||||
if (isLogDeleted) {
|
Toast.makeText(latinIME, R.string.notify_logging_suspended,
|
||||||
Toast.makeText(latinIME, R.string.notify_session_log_deleted,
|
Toast.LENGTH_LONG).show();
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(latinIME,
|
|
||||||
R.string.notify_session_log_not_deleted, Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -328,13 +385,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAllowedToLog() {
|
private boolean isAllowedToLog() {
|
||||||
return !mIsPasswordView && !mIsLoggingSuspended;
|
return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestIndicatorRedraw() {
|
public void requestIndicatorRedraw() {
|
||||||
// invalidate any existing graphics
|
// invalidate any existing graphics
|
||||||
if (mKeyboardSwitcher != null) {
|
if (IS_SHOWING_INDICATOR) {
|
||||||
mKeyboardSwitcher.getKeyboardView().invalidateAllKeys();
|
if (mKeyboardSwitcher != null) {
|
||||||
|
mKeyboardSwitcher.getKeyboardView().invalidateAllKeys();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,6 +526,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) {
|
private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) {
|
||||||
|
if (!isAllowedToLog()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mMainResearchLog == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (isPrivacySensitive) {
|
if (isPrivacySensitive) {
|
||||||
mMainResearchLog.publishPublicEvents(logUnit);
|
mMainResearchLog.publishPublicEvents(logUnit);
|
||||||
} else {
|
} else {
|
||||||
|
@ -536,6 +601,18 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getUUID(final SharedPreferences prefs) {
|
||||||
|
String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
|
||||||
|
if (null == uuidString) {
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
uuidString = uuid.toString();
|
||||||
|
Editor editor = prefs.edit();
|
||||||
|
editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
return uuidString;
|
||||||
|
}
|
||||||
|
|
||||||
private String scrubWord(String word) {
|
private String scrubWord(String word) {
|
||||||
if (mDictionary == null) {
|
if (mDictionary == null) {
|
||||||
return WORD_REPLACEMENT_STRING;
|
return WORD_REPLACEMENT_STRING;
|
||||||
|
@ -546,9 +623,62 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
return WORD_REPLACEMENT_STRING;
|
return WORD_REPLACEMENT_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special methods related to startup, shutdown, logging itself
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_INTENTIONAL_LOG = {
|
private static final String[] EVENTKEYS_INTENTIONAL_LOG = {
|
||||||
"IntentionalLog"
|
"IntentionalLog"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
|
||||||
|
"LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
|
||||||
|
"fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
|
||||||
|
};
|
||||||
|
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
|
||||||
|
final SharedPreferences prefs) {
|
||||||
|
final ResearchLogger researchLogger = getInstance();
|
||||||
|
researchLogger.start();
|
||||||
|
if (editorInfo != null) {
|
||||||
|
final Context context = researchLogger.mContext;
|
||||||
|
try {
|
||||||
|
final PackageInfo packageInfo;
|
||||||
|
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
||||||
|
0);
|
||||||
|
final Integer versionCode = packageInfo.versionCode;
|
||||||
|
final String versionName = packageInfo.versionName;
|
||||||
|
final Object[] values = {
|
||||||
|
researchLogger.mUUIDString, editorInfo.packageName,
|
||||||
|
Integer.toHexString(editorInfo.inputType),
|
||||||
|
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
|
||||||
|
Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
|
||||||
|
OUTPUT_FORMAT_VERSION
|
||||||
|
};
|
||||||
|
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void latinIME_onFinishInputInternal() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = {
|
||||||
|
"LatinIMECommitText", "typedWord"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void latinIME_commitText(final CharSequence typedWord) {
|
||||||
|
final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
|
||||||
|
final Object[] values = {
|
||||||
|
scrubbedWord
|
||||||
|
};
|
||||||
|
final ResearchLogger researchLogger = getInstance();
|
||||||
|
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
|
||||||
|
researchLogger.onWordComplete(scrubbedWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular logging methods
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
|
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
|
||||||
"LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
|
"LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
|
||||||
"pressure"
|
"pressure"
|
||||||
|
@ -611,19 +741,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
|
EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINIME_COMMITTEXT = {
|
|
||||||
"LatinIMECommitText", "typedWord"
|
|
||||||
};
|
|
||||||
public static void latinIME_commitText(final CharSequence typedWord) {
|
|
||||||
final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
|
|
||||||
final Object[] values = {
|
|
||||||
scrubbedWord
|
|
||||||
};
|
|
||||||
final ResearchLogger researchLogger = getInstance();
|
|
||||||
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
|
|
||||||
researchLogger.onWordComplete(scrubbedWord);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
|
private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
|
||||||
"LatinIMEDeleteSurroundingText", "length"
|
"LatinIMEDeleteSurroundingText", "length"
|
||||||
};
|
};
|
||||||
|
@ -702,51 +819,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
// Play it safe. Remove privacy-sensitive events.
|
// Play it safe. Remove privacy-sensitive events.
|
||||||
researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true);
|
researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true);
|
||||||
researchLogger.mCurrentLogUnit = new LogUnit();
|
researchLogger.mCurrentLogUnit = new LogUnit();
|
||||||
|
getInstance().restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
|
|
||||||
"LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
|
|
||||||
"fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
|
|
||||||
};
|
|
||||||
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
|
|
||||||
final SharedPreferences prefs) {
|
|
||||||
final ResearchLogger researchLogger = getInstance();
|
|
||||||
researchLogger.start();
|
|
||||||
if (editorInfo != null) {
|
|
||||||
final Context context = researchLogger.mContext;
|
|
||||||
try {
|
|
||||||
final PackageInfo packageInfo;
|
|
||||||
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
|
||||||
0);
|
|
||||||
final Integer versionCode = packageInfo.versionCode;
|
|
||||||
final String versionName = packageInfo.versionName;
|
|
||||||
final Object[] values = {
|
|
||||||
researchLogger.mUUIDString, editorInfo.packageName,
|
|
||||||
Integer.toHexString(editorInfo.inputType),
|
|
||||||
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
|
|
||||||
Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
|
|
||||||
OUTPUT_FORMAT_VERSION
|
|
||||||
};
|
|
||||||
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getUUID(final SharedPreferences prefs) {
|
|
||||||
String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
|
|
||||||
if (null == uuidString) {
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
uuidString = uuid.toString();
|
|
||||||
Editor editor = prefs.edit();
|
|
||||||
editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
return uuidString;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
|
private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
|
||||||
"LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
|
"LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
|
||||||
"oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
|
"oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
|
||||||
|
@ -873,6 +949,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
||||||
if (keyboard != null) {
|
if (keyboard != null) {
|
||||||
final KeyboardId kid = keyboard.mId;
|
final KeyboardId kid = keyboard.mId;
|
||||||
final boolean isPasswordView = kid.passwordInput();
|
final boolean isPasswordView = kid.passwordInput();
|
||||||
|
getInstance().setIsPasswordView(isPasswordView);
|
||||||
final Object[] values = {
|
final Object[] values = {
|
||||||
KeyboardId.elementIdToName(kid.mElementId),
|
KeyboardId.elementIdToName(kid.mElementId),
|
||||||
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
|
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
|
||||||
|
|
|
@ -208,7 +208,8 @@ public class Settings extends InputMethodSettingsFragment
|
||||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||||
if (usabilityStudyPref instanceof CheckBoxPreference) {
|
if (usabilityStudyPref instanceof CheckBoxPreference) {
|
||||||
CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
|
CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
|
||||||
checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true));
|
checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
|
||||||
|
ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
|
||||||
checkbox.setSummary(R.string.settings_warning_researcher_mode);
|
checkbox.setSummary(R.string.settings_warning_researcher_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue