2012-03-22 02:13:33 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2012-07-20 18:02:39 +00:00
|
|
|
package com.android.inputmethod.research;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-05-29 16:33:09 +00:00
|
|
|
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
|
|
|
|
|
2012-08-05 06:26:35 +00:00
|
|
|
import android.app.AlarmManager;
|
2012-06-04 19:27:37 +00:00
|
|
|
import android.app.AlertDialog;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.app.Dialog;
|
2012-08-05 06:26:35 +00:00
|
|
|
import android.app.PendingIntent;
|
2012-07-13 21:25:29 +00:00
|
|
|
import android.content.Context;
|
2012-06-04 19:27:37 +00:00
|
|
|
import android.content.DialogInterface;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.content.DialogInterface.OnCancelListener;
|
2012-08-05 06:26:35 +00:00
|
|
|
import android.content.Intent;
|
2012-03-22 02:13:33 +00:00
|
|
|
import android.content.SharedPreferences;
|
2012-05-02 21:33:48 +00:00
|
|
|
import android.content.SharedPreferences.Editor;
|
2012-07-13 21:25:29 +00:00
|
|
|
import android.content.pm.PackageInfo;
|
|
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
2012-07-19 02:21:54 +00:00
|
|
|
import android.graphics.Canvas;
|
|
|
|
import android.graphics.Color;
|
|
|
|
import android.graphics.Paint;
|
|
|
|
import android.graphics.Paint.Style;
|
2012-07-26 22:27:57 +00:00
|
|
|
import android.net.Uri;
|
2012-04-18 03:54:33 +00:00
|
|
|
import android.os.Build;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.os.IBinder;
|
2012-07-31 04:55:42 +00:00
|
|
|
import android.os.SystemClock;
|
2013-01-07 09:40:59 +00:00
|
|
|
import android.preference.PreferenceManager;
|
2012-03-22 02:13:33 +00:00
|
|
|
import android.text.TextUtils;
|
2012-07-13 22:35:26 +00:00
|
|
|
import android.text.format.DateUtils;
|
2012-03-22 02:13:33 +00:00
|
|
|
import android.util.Log;
|
2012-08-02 00:26:45 +00:00
|
|
|
import android.view.KeyEvent;
|
2012-03-22 02:13:33 +00:00
|
|
|
import android.view.MotionEvent;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.view.Window;
|
|
|
|
import android.view.WindowManager;
|
2012-03-30 20:15:46 +00:00
|
|
|
import android.view.inputmethod.CompletionInfo;
|
|
|
|
import android.view.inputmethod.EditorInfo;
|
2012-05-02 05:09:53 +00:00
|
|
|
import android.view.inputmethod.InputConnection;
|
2012-06-04 19:27:37 +00:00
|
|
|
import android.widget.Toast;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-03-30 20:15:46 +00:00
|
|
|
import com.android.inputmethod.keyboard.Key;
|
2012-03-22 02:13:33 +00:00
|
|
|
import com.android.inputmethod.keyboard.Keyboard;
|
2012-05-29 16:33:09 +00:00
|
|
|
import com.android.inputmethod.keyboard.KeyboardId;
|
2012-07-19 02:21:54 +00:00
|
|
|
import com.android.inputmethod.keyboard.KeyboardView;
|
2012-07-23 01:27:14 +00:00
|
|
|
import com.android.inputmethod.keyboard.MainKeyboardView;
|
2012-08-21 05:05:57 +00:00
|
|
|
import com.android.inputmethod.latin.Constants;
|
2012-07-20 18:02:39 +00:00
|
|
|
import com.android.inputmethod.latin.Dictionary;
|
2012-08-09 03:28:48 +00:00
|
|
|
import com.android.inputmethod.latin.InputTypeUtils;
|
2012-07-20 18:02:39 +00:00
|
|
|
import com.android.inputmethod.latin.LatinIME;
|
|
|
|
import com.android.inputmethod.latin.R;
|
|
|
|
import com.android.inputmethod.latin.RichInputConnection;
|
2012-06-12 17:56:03 +00:00
|
|
|
import com.android.inputmethod.latin.RichInputConnection.Range;
|
2012-07-20 18:02:39 +00:00
|
|
|
import com.android.inputmethod.latin.Suggest;
|
|
|
|
import com.android.inputmethod.latin.SuggestedWords;
|
2012-03-30 20:15:46 +00:00
|
|
|
import com.android.inputmethod.latin.define.ProductionFlag;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
|
|
|
import java.io.File;
|
2012-05-14 16:41:39 +00:00
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.Locale;
|
2012-05-02 21:33:48 +00:00
|
|
|
import java.util.UUID;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Logs the use of the LatinIME keyboard.
|
|
|
|
*
|
|
|
|
* This class logs operations on the IME keyboard, including what the user has typed.
|
|
|
|
* Data is stored locally in a file in app-specific storage.
|
|
|
|
*
|
2012-05-25 10:04:54 +00:00
|
|
|
* This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
|
2012-03-22 02:13:33 +00:00
|
|
|
*/
|
|
|
|
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
|
|
private static final String TAG = ResearchLogger.class.getSimpleName();
|
2012-12-18 02:19:58 +00:00
|
|
|
private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
|
2012-12-22 21:57:58 +00:00
|
|
|
// 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;
|
2012-07-20 18:02:39 +00:00
|
|
|
public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
|
2012-05-14 16:41:39 +00:00
|
|
|
/* package */ static boolean sIsLogging = false;
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final int OUTPUT_FORMAT_VERSION = 5;
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
|
2012-07-19 01:41:15 +00:00
|
|
|
private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash";
|
2012-07-18 20:52:41 +00:00
|
|
|
/* package */ static final String FILENAME_PREFIX = "researchLog";
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String FILENAME_SUFFIX = ".txt";
|
|
|
|
private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
|
2012-06-29 14:02:39 +00:00
|
|
|
new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
|
2012-08-14 18:08:17 +00:00
|
|
|
// 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.
|
2012-07-19 02:21:54 +00:00
|
|
|
private static final boolean IS_SHOWING_INDICATOR = true;
|
2012-08-14 18:08:17 +00:00
|
|
|
// 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;
|
2012-08-03 03:22:29 +00:00
|
|
|
public static final int FEEDBACK_WORD_BUFFER_SIZE = 5;
|
2012-05-14 16:41:39 +00:00
|
|
|
|
|
|
|
// constants related to specific log points
|
2012-05-09 23:04:26 +00:00
|
|
|
private static final String WHITESPACE_SEPARATORS = " \t\n\r";
|
2012-05-02 05:09:53 +00:00
|
|
|
private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid";
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final ResearchLogger sInstance = new ResearchLogger();
|
|
|
|
// to write to a different filename, e.g., for testing, set mFile before calling start()
|
2012-06-29 14:02:39 +00:00
|
|
|
/* package */ File mFilesDir;
|
|
|
|
/* package */ String mUUIDString;
|
|
|
|
/* package */ ResearchLog mMainResearchLog;
|
2012-08-03 03:22:29 +00:00
|
|
|
// mFeedbackLog records all events for the session, private or not (excepting
|
2012-06-29 14:02:39 +00:00
|
|
|
// passwords). It is written to permanent storage only if the user explicitly commands
|
|
|
|
// the system to do so.
|
2012-08-03 03:22:29 +00:00
|
|
|
// LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are
|
|
|
|
// complete.
|
|
|
|
/* package */ ResearchLog mFeedbackLog;
|
|
|
|
/* package */ MainLogBuffer mMainLogBuffer;
|
|
|
|
/* package */ LogBuffer mFeedbackLogBuffer;
|
2012-06-29 14:02:39 +00:00
|
|
|
|
2012-06-13 23:37:20 +00:00
|
|
|
private boolean mIsPasswordView = false;
|
2012-06-29 14:02:39 +00:00
|
|
|
private boolean mIsLoggingSuspended = false;
|
2012-07-18 20:52:41 +00:00
|
|
|
private SharedPreferences mPrefs;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-06-15 05:15:27 +00:00
|
|
|
// digits entered by the user are replaced with this codepoint.
|
|
|
|
/* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
|
|
|
|
Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area"
|
2012-06-16 00:49:42 +00:00
|
|
|
// U+E001 is in the "private-use area"
|
|
|
|
/* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
|
2012-07-13 22:35:26 +00:00
|
|
|
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 MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
|
2012-07-18 20:52:41 +00:00
|
|
|
protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
|
2012-05-14 16:41:39 +00:00
|
|
|
// set when LatinIME should ignore an onUpdateSelection() callback that
|
|
|
|
// arises from operations in this class
|
|
|
|
private static boolean sLatinIMEExpectingUpdateSelection = false;
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-06-16 00:49:42 +00:00
|
|
|
// used to check whether words are not unique
|
|
|
|
private Suggest mSuggest;
|
2012-08-21 06:11:02 +00:00
|
|
|
private MainKeyboardView mMainKeyboardView;
|
2012-12-22 22:18:16 +00:00
|
|
|
private LatinIME mLatinIME;
|
2012-07-31 04:55:42 +00:00
|
|
|
private final Statistics mStatistics;
|
2012-08-05 06:26:35 +00:00
|
|
|
|
|
|
|
private Intent mUploadIntent;
|
2012-07-19 00:05:48 +00:00
|
|
|
|
2012-08-03 03:22:29 +00:00
|
|
|
private LogUnit mCurrentLogUnit = new LogUnit();
|
|
|
|
|
2012-08-31 02:09:28 +00:00
|
|
|
// Gestured or tapped words may be committed after the gesture of the next word has started.
|
|
|
|
// To ensure that the gesture data of the next word is not associated with the previous word,
|
|
|
|
// thereby leaking private data, we store the time of the down event that started the second
|
|
|
|
// gesture, and when committing the earlier word, split the LogUnit.
|
|
|
|
private long mSavedDownEventTime;
|
2012-05-14 16:41:39 +00:00
|
|
|
private ResearchLogger() {
|
2012-07-31 04:55:42 +00:00
|
|
|
mStatistics = Statistics.getInstance();
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
public static ResearchLogger getInstance() {
|
|
|
|
return sInstance;
|
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2013-01-07 09:40:59 +00:00
|
|
|
public void init(final LatinIME latinIME) {
|
2012-12-22 22:18:16 +00:00
|
|
|
assert latinIME != null;
|
|
|
|
if (latinIME == null) {
|
2012-05-14 16:41:39 +00:00
|
|
|
Log.w(TAG, "IMS is null; logging is off");
|
|
|
|
} else {
|
2012-12-22 22:18:16 +00:00
|
|
|
mFilesDir = latinIME.getFilesDir();
|
2012-05-14 16:41:39 +00:00
|
|
|
if (mFilesDir == null || !mFilesDir.exists()) {
|
|
|
|
Log.w(TAG, "IME storage directory does not exist.");
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
}
|
2013-01-07 09:40:59 +00:00
|
|
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME);
|
2012-05-14 16:41:39 +00:00
|
|
|
if (prefs != null) {
|
2012-06-29 14:02:39 +00:00
|
|
|
mUUIDString = getUUID(prefs);
|
2012-07-18 20:52:41 +00:00
|
|
|
if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
|
|
|
|
Editor e = prefs.edit();
|
|
|
|
e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE);
|
|
|
|
e.apply();
|
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
2012-06-04 19:27:37 +00:00
|
|
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
2012-07-13 22:35:26 +00:00
|
|
|
|
|
|
|
final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L);
|
|
|
|
final long now = System.currentTimeMillis();
|
|
|
|
if (lastCleanupTime + DURATION_BETWEEN_DIR_CLEANUP_IN_MS < now) {
|
|
|
|
final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS;
|
|
|
|
cleanupLoggingDir(mFilesDir, timeHorizon);
|
|
|
|
Editor e = prefs.edit();
|
|
|
|
e.putLong(PREF_LAST_CLEANUP_TIME, now);
|
|
|
|
e.apply();
|
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-12-22 22:18:16 +00:00
|
|
|
mLatinIME = latinIME;
|
2012-07-18 20:52:41 +00:00
|
|
|
mPrefs = prefs;
|
2012-12-22 22:18:16 +00:00
|
|
|
mUploadIntent = new Intent(mLatinIME, UploaderService.class);
|
2012-08-05 06:26:35 +00:00
|
|
|
|
|
|
|
if (ProductionFlag.IS_EXPERIMENTAL) {
|
2012-12-22 22:18:16 +00:00
|
|
|
scheduleUploadingService(mLatinIME);
|
2012-08-05 06:26:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Arrange for the UploaderService to be run on a regular basis.
|
|
|
|
*
|
|
|
|
* Any existing scheduled invocation of UploaderService is removed and rescheduled. This may
|
|
|
|
* cause problems if this method is called often and frequent updates are required, but since
|
|
|
|
* the user will likely be sleeping at some point, if the interval is less that the expected
|
|
|
|
* sleep duration and this method is not called during that time, the service should be invoked
|
|
|
|
* at some point.
|
|
|
|
*/
|
|
|
|
public static void scheduleUploadingService(Context context) {
|
|
|
|
final Intent intent = new Intent(context, UploaderService.class);
|
|
|
|
final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
|
|
|
final AlarmManager manager =
|
|
|
|
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
|
|
|
manager.cancel(pendingIntent);
|
|
|
|
manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
|
|
|
UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent);
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
2012-07-13 22:35:26 +00:00
|
|
|
private void cleanupLoggingDir(final File dir, final long time) {
|
|
|
|
for (File file : dir.listFiles()) {
|
|
|
|
if (file.getName().startsWith(ResearchLogger.FILENAME_PREFIX) &&
|
|
|
|
file.lastModified() < time) {
|
|
|
|
file.delete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-21 06:11:02 +00:00
|
|
|
public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) {
|
|
|
|
mMainKeyboardView = mainKeyboardView;
|
2012-07-19 01:41:15 +00:00
|
|
|
maybeShowSplashScreen();
|
|
|
|
}
|
|
|
|
|
2012-08-21 06:11:02 +00:00
|
|
|
public void mainKeyboardView_onDetachedFromWindow() {
|
|
|
|
mMainKeyboardView = null;
|
|
|
|
}
|
|
|
|
|
2012-07-19 01:41:15 +00:00
|
|
|
private boolean hasSeenSplash() {
|
|
|
|
return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Dialog mSplashDialog = null;
|
|
|
|
|
|
|
|
private void maybeShowSplashScreen() {
|
|
|
|
if (hasSeenSplash()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mSplashDialog != null && mSplashDialog.isShowing()) {
|
|
|
|
return;
|
|
|
|
}
|
2012-08-21 06:11:02 +00:00
|
|
|
final IBinder windowToken = mMainKeyboardView != null
|
|
|
|
? mMainKeyboardView.getWindowToken() : null;
|
2012-07-19 01:41:15 +00:00
|
|
|
if (windowToken == null) {
|
|
|
|
return;
|
|
|
|
}
|
2012-12-22 22:18:16 +00:00
|
|
|
final AlertDialog.Builder builder = new AlertDialog.Builder(mLatinIME)
|
2012-07-26 22:27:57 +00:00
|
|
|
.setTitle(R.string.research_splash_title)
|
|
|
|
.setMessage(R.string.research_splash_content)
|
|
|
|
.setPositiveButton(android.R.string.yes,
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
onUserLoggingConsent();
|
|
|
|
mSplashDialog.dismiss();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.setNegativeButton(android.R.string.no,
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
2012-12-22 22:18:16 +00:00
|
|
|
final String packageName = mLatinIME.getPackageName();
|
2012-07-26 22:27:57 +00:00
|
|
|
final Uri packageUri = Uri.parse("package:" + packageName);
|
|
|
|
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE,
|
|
|
|
packageUri);
|
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
2012-12-22 22:18:16 +00:00
|
|
|
mLatinIME.startActivity(intent);
|
2012-07-26 22:27:57 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.setCancelable(true)
|
|
|
|
.setOnCancelListener(
|
|
|
|
new OnCancelListener() {
|
|
|
|
@Override
|
|
|
|
public void onCancel(DialogInterface dialog) {
|
2012-12-22 22:18:16 +00:00
|
|
|
mLatinIME.requestHideSelf(0);
|
2012-07-26 22:27:57 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
mSplashDialog = builder.create();
|
2012-07-19 01:41:15 +00:00
|
|
|
final Window w = mSplashDialog.getWindow();
|
|
|
|
final WindowManager.LayoutParams lp = w.getAttributes();
|
|
|
|
lp.token = windowToken;
|
|
|
|
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
|
|
|
|
w.setAttributes(lp);
|
|
|
|
w.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
|
|
|
mSplashDialog.show();
|
|
|
|
}
|
|
|
|
|
2012-07-26 22:27:57 +00:00
|
|
|
public void onUserLoggingConsent() {
|
|
|
|
setLoggingAllowed(true);
|
2012-07-19 01:41:15 +00:00
|
|
|
if (mPrefs == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final Editor e = mPrefs.edit();
|
|
|
|
e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true);
|
|
|
|
e.apply();
|
2012-08-05 06:26:35 +00:00
|
|
|
restart();
|
2012-07-19 01:41:15 +00:00
|
|
|
}
|
|
|
|
|
2012-08-03 03:22:29 +00:00
|
|
|
private void setLoggingAllowed(boolean enableLogging) {
|
|
|
|
if (mPrefs == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Editor e = mPrefs.edit();
|
|
|
|
e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
|
|
|
|
e.apply();
|
|
|
|
sIsLogging = enableLogging;
|
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
private File createLogFile(File filesDir) {
|
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append(FILENAME_PREFIX).append('-');
|
|
|
|
sb.append(mUUIDString).append('-');
|
|
|
|
sb.append(TIMESTAMP_DATEFORMAT.format(new Date()));
|
|
|
|
sb.append(FILENAME_SUFFIX);
|
|
|
|
return new File(filesDir, sb.toString());
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
2012-04-10 01:39:35 +00:00
|
|
|
|
2012-07-31 04:55:42 +00:00
|
|
|
private void checkForEmptyEditor() {
|
2012-12-22 22:18:16 +00:00
|
|
|
if (mLatinIME == null) {
|
2012-07-31 04:55:42 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-12-22 22:18:16 +00:00
|
|
|
final InputConnection ic = mLatinIME.getCurrentInputConnection();
|
2012-07-31 04:55:42 +00:00
|
|
|
if (ic == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final CharSequence textBefore = ic.getTextBeforeCursor(1, 0);
|
|
|
|
if (!TextUtils.isEmpty(textBefore)) {
|
|
|
|
mStatistics.setIsEmptyUponStarting(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final CharSequence textAfter = ic.getTextAfterCursor(1, 0);
|
|
|
|
if (!TextUtils.isEmpty(textAfter)) {
|
|
|
|
mStatistics.setIsEmptyUponStarting(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (textBefore != null && textAfter != null) {
|
|
|
|
mStatistics.setIsEmptyUponStarting(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
private void start() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "start called");
|
|
|
|
}
|
2012-07-19 01:41:15 +00:00
|
|
|
maybeShowSplashScreen();
|
2012-07-18 20:52:41 +00:00
|
|
|
updateSuspendedState();
|
|
|
|
requestIndicatorRedraw();
|
2012-07-31 04:55:42 +00:00
|
|
|
mStatistics.reset();
|
|
|
|
checkForEmptyEditor();
|
2012-07-18 20:52:41 +00:00
|
|
|
if (!isAllowedToLog()) {
|
2012-06-04 19:27:37 +00:00
|
|
|
// Log.w(TAG, "not in usability mode; not logging");
|
|
|
|
return;
|
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
if (mFilesDir == null || !mFilesDir.exists()) {
|
|
|
|
Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
|
2012-06-29 14:02:39 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mMainLogBuffer == null) {
|
|
|
|
mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
|
|
|
mMainLogBuffer = new MainLogBuffer(mMainResearchLog);
|
|
|
|
mMainLogBuffer.setSuggest(mSuggest);
|
|
|
|
}
|
|
|
|
if (mFeedbackLogBuffer == null) {
|
|
|
|
mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
|
|
|
|
// LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold
|
|
|
|
// the feedback LogUnit itself.
|
2012-12-23 18:40:34 +00:00
|
|
|
mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1);
|
2012-04-10 01:39:35 +00:00
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
/* package */ void stop() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "stop called");
|
|
|
|
}
|
2012-08-10 08:54:06 +00:00
|
|
|
commitCurrentLogUnit();
|
2012-07-31 04:55:42 +00:00
|
|
|
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mMainLogBuffer != null) {
|
2012-08-12 20:54:53 +00:00
|
|
|
publishLogBuffer(mMainLogBuffer, mMainResearchLog,
|
|
|
|
LOG_EVERYTHING /* isIncludingPrivateData */);
|
2012-08-08 16:38:24 +00:00
|
|
|
mMainResearchLog.close(null /* callback */);
|
2012-08-03 03:22:29 +00:00
|
|
|
mMainLogBuffer = null;
|
2012-07-18 20:52:41 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mFeedbackLogBuffer != null) {
|
2012-08-08 16:38:24 +00:00
|
|
|
mFeedbackLog.close(null /* callback */);
|
2012-08-03 03:22:29 +00:00
|
|
|
mFeedbackLogBuffer = null;
|
2012-07-18 20:52:41 +00:00
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean abort() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "abort called");
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
boolean didAbortMainLog = false;
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mMainLogBuffer != null) {
|
|
|
|
mMainLogBuffer.clear();
|
2012-04-18 03:54:33 +00:00
|
|
|
try {
|
2012-08-03 03:22:29 +00:00
|
|
|
didAbortMainLog = mMainResearchLog.blockingAbort();
|
2012-05-14 16:41:39 +00:00
|
|
|
} catch (InterruptedException e) {
|
2012-08-03 03:22:29 +00:00
|
|
|
// Don't know whether this succeeded or not. We assume not; this is reported
|
|
|
|
// to the caller.
|
2012-04-18 03:54:33 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
mMainLogBuffer = null;
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
boolean didAbortFeedbackLog = false;
|
|
|
|
if (mFeedbackLogBuffer != null) {
|
|
|
|
mFeedbackLogBuffer.clear();
|
2012-06-04 19:27:37 +00:00
|
|
|
try {
|
2012-08-03 03:22:29 +00:00
|
|
|
didAbortFeedbackLog = mFeedbackLog.blockingAbort();
|
2012-06-29 14:02:39 +00:00
|
|
|
} catch (InterruptedException e) {
|
2012-08-03 03:22:29 +00:00
|
|
|
// Don't know whether this succeeded or not. We assume not; this is reported
|
|
|
|
// to the caller.
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
mFeedbackLogBuffer = null;
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
return didAbortMainLog && didAbortFeedbackLog;
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
private void restart() {
|
|
|
|
stop();
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
private long mResumeTime = 0L;
|
|
|
|
private void updateSuspendedState() {
|
|
|
|
final long time = System.currentTimeMillis();
|
|
|
|
if (time > mResumeTime) {
|
|
|
|
mIsLoggingSuspended = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
@Override
|
|
|
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
|
|
|
if (key == null || prefs == null) {
|
|
|
|
return;
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
|
2012-06-04 19:27:37 +00:00
|
|
|
if (sIsLogging == false) {
|
|
|
|
abort();
|
|
|
|
}
|
2012-07-18 20:52:41 +00:00
|
|
|
requestIndicatorRedraw();
|
2012-08-07 01:29:51 +00:00
|
|
|
mPrefs = prefs;
|
|
|
|
prefsChanged(prefs);
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
|
|
|
|
2012-07-26 22:39:55 +00:00
|
|
|
public void onResearchKeySelected(final LatinIME latinIME) {
|
2012-07-19 01:00:48 +00:00
|
|
|
if (mInFeedbackDialog) {
|
|
|
|
Toast.makeText(latinIME, R.string.research_please_exit_feedback_form,
|
|
|
|
Toast.LENGTH_LONG).show();
|
|
|
|
return;
|
|
|
|
}
|
2012-07-26 22:39:55 +00:00
|
|
|
presentFeedbackDialog(latinIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: currently unreachable. Remove after being sure no menu is needed.
|
|
|
|
/*
|
|
|
|
public void presentResearchDialog(final LatinIME latinIME) {
|
2012-06-04 19:27:37 +00:00
|
|
|
final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
|
2012-07-18 20:52:41 +00:00
|
|
|
final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
|
2012-06-04 19:27:37 +00:00
|
|
|
final CharSequence[] items = new CharSequence[] {
|
2012-07-19 01:00:48 +00:00
|
|
|
latinIME.getString(R.string.research_feedback_menu_option),
|
|
|
|
showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
|
|
|
|
latinIME.getString(R.string.research_do_not_log_this_session)
|
2012-06-04 19:27:37 +00:00
|
|
|
};
|
|
|
|
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface di, int position) {
|
|
|
|
di.dismiss();
|
|
|
|
switch (position) {
|
|
|
|
case 0:
|
2012-07-19 01:00:48 +00:00
|
|
|
presentFeedbackDialog(latinIME);
|
2012-06-04 19:27:37 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2012-07-26 22:39:55 +00:00
|
|
|
enableOrDisable(showEnable, latinIME);
|
2012-06-29 14:02:39 +00:00
|
|
|
break;
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
|
2012-06-04 19:27:37 +00:00
|
|
|
};
|
|
|
|
final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME)
|
|
|
|
.setItems(items, listener)
|
|
|
|
.setTitle(title);
|
|
|
|
latinIME.showOptionDialog(builder.create());
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-07-26 22:39:55 +00:00
|
|
|
*/
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-07-19 01:00:48 +00:00
|
|
|
private boolean mInFeedbackDialog = false;
|
|
|
|
public void presentFeedbackDialog(LatinIME latinIME) {
|
|
|
|
mInFeedbackDialog = true;
|
|
|
|
latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
|
|
|
|
}
|
|
|
|
|
2012-07-26 22:39:55 +00:00
|
|
|
// TODO: currently unreachable. Remove after being sure enable/disable is
|
|
|
|
// not needed.
|
|
|
|
/*
|
|
|
|
public void enableOrDisable(final boolean showEnable, final LatinIME latinIME) {
|
|
|
|
if (showEnable) {
|
|
|
|
if (!sIsLogging) {
|
|
|
|
setLoggingAllowed(true);
|
|
|
|
}
|
|
|
|
resumeLogging();
|
|
|
|
Toast.makeText(latinIME,
|
|
|
|
R.string.research_notify_session_logging_enabled,
|
|
|
|
Toast.LENGTH_LONG).show();
|
|
|
|
} else {
|
|
|
|
Toast toast = Toast.makeText(latinIME,
|
|
|
|
R.string.research_notify_session_log_deleting,
|
|
|
|
Toast.LENGTH_LONG);
|
|
|
|
toast.show();
|
|
|
|
boolean isLogDeleted = abort();
|
|
|
|
final long currentTime = System.currentTimeMillis();
|
|
|
|
final long resumeTime = currentTime + 1000 * 60 *
|
|
|
|
SUSPEND_DURATION_IN_MINUTES;
|
|
|
|
suspendLoggingUntil(resumeTime);
|
|
|
|
toast.cancel();
|
|
|
|
Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
|
|
|
|
Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2012-08-10 08:54:06 +00:00
|
|
|
static class LogStatement {
|
|
|
|
final String mName;
|
|
|
|
|
|
|
|
// mIsPotentiallyPrivate indicates that event contains potentially private information. If
|
|
|
|
// the word that this event is a part of is determined to be privacy-sensitive, then this
|
|
|
|
// event should not be included in the output log. The system waits to output until the
|
|
|
|
// containing word is known.
|
|
|
|
final boolean mIsPotentiallyPrivate;
|
|
|
|
|
|
|
|
// mIsPotentiallyRevealing indicates that this statement may disclose details about other
|
|
|
|
// words typed in other LogUnits. This can happen if the user is not inserting spaces, and
|
|
|
|
// data from Suggestions and/or Composing text reveals the entire "megaword". For example,
|
|
|
|
// say the user is typing "for the win", and the system wants to record the bigram "the
|
|
|
|
// win". If the user types "forthe", omitting the space, the system will give "for the" as
|
|
|
|
// a suggestion. If the user accepts the autocorrection, the suggestion for "for the" is
|
|
|
|
// included in the log for the word "the", disclosing that the previous word had been "for".
|
|
|
|
// For now, we simply do not include this data when logging part of a "megaword".
|
|
|
|
final boolean mIsPotentiallyRevealing;
|
|
|
|
|
|
|
|
// mKeys stores the names that are the attributes in the output json objects
|
|
|
|
final String[] mKeys;
|
|
|
|
private static final String[] NULL_KEYS = new String[0];
|
|
|
|
|
|
|
|
LogStatement(final String name, final boolean isPotentiallyPrivate,
|
|
|
|
final boolean isPotentiallyRevealing, final String... keys) {
|
|
|
|
mName = name;
|
|
|
|
mIsPotentiallyPrivate = isPotentiallyPrivate;
|
|
|
|
mIsPotentiallyRevealing = isPotentiallyRevealing;
|
|
|
|
mKeys = (keys == null) ? NULL_KEYS : keys;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final LogStatement LOGSTATEMENT_FEEDBACK =
|
|
|
|
new LogStatement("UserTimestamp", false, false, "contents");
|
2012-07-19 01:00:48 +00:00
|
|
|
public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mFeedbackLogBuffer == null) {
|
|
|
|
return;
|
|
|
|
}
|
2012-08-05 06:26:35 +00:00
|
|
|
if (includeHistory) {
|
2012-08-10 08:54:06 +00:00
|
|
|
commitCurrentLogUnit();
|
2012-08-05 06:26:35 +00:00
|
|
|
} else {
|
2012-08-03 03:22:29 +00:00
|
|
|
mFeedbackLogBuffer.clear();
|
2012-07-19 01:00:48 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
final LogUnit feedbackLogUnit = new LogUnit();
|
2012-08-10 21:21:18 +00:00
|
|
|
feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(),
|
|
|
|
feedbackContents);
|
2012-08-03 03:22:29 +00:00
|
|
|
mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
|
|
|
|
publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */);
|
2012-08-08 16:38:24 +00:00
|
|
|
mFeedbackLog.close(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
uploadNow();
|
|
|
|
}
|
|
|
|
});
|
2012-08-03 03:22:29 +00:00
|
|
|
mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
|
2012-07-19 01:00:48 +00:00
|
|
|
}
|
|
|
|
|
2012-08-05 06:26:35 +00:00
|
|
|
public void uploadNow() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "calling uploadNow()");
|
|
|
|
}
|
2012-12-22 22:18:16 +00:00
|
|
|
mLatinIME.startService(mUploadIntent);
|
2012-08-05 06:26:35 +00:00
|
|
|
}
|
|
|
|
|
2012-07-19 01:00:48 +00:00
|
|
|
public void onLeavingSendFeedbackDialog() {
|
|
|
|
mInFeedbackDialog = false;
|
|
|
|
}
|
|
|
|
|
2012-06-16 00:49:42 +00:00
|
|
|
public void initSuggest(Suggest suggest) {
|
|
|
|
mSuggest = suggest;
|
2012-08-03 03:22:29 +00:00
|
|
|
if (mMainLogBuffer != null) {
|
|
|
|
mMainLogBuffer.setSuggest(mSuggest);
|
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-08-13 15:24:40 +00:00
|
|
|
private Dictionary getDictionary() {
|
|
|
|
if (mSuggest == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return mSuggest.getMainDictionary();
|
|
|
|
}
|
|
|
|
|
2012-06-13 23:37:20 +00:00
|
|
|
private void setIsPasswordView(boolean isPasswordView) {
|
|
|
|
mIsPasswordView = isPasswordView;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isAllowedToLog() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "iatl: " +
|
|
|
|
"mipw=" + mIsPasswordView +
|
|
|
|
", mils=" + mIsLoggingSuspended +
|
|
|
|
", sil=" + sIsLogging +
|
|
|
|
", mInFeedbackDialog=" + mInFeedbackDialog);
|
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog;
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void requestIndicatorRedraw() {
|
2012-07-19 02:21:54 +00:00
|
|
|
if (!IS_SHOWING_INDICATOR) {
|
|
|
|
return;
|
|
|
|
}
|
2012-08-21 06:11:02 +00:00
|
|
|
if (mMainKeyboardView == null) {
|
2012-07-19 02:21:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-08-21 06:11:02 +00:00
|
|
|
mMainKeyboardView.invalidateAllKeys();
|
2012-07-19 02:21:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width,
|
|
|
|
int height) {
|
|
|
|
// TODO: Reimplement using a keyboard background image specific to the ResearchLogger
|
|
|
|
// and remove this method.
|
2012-07-23 01:27:14 +00:00
|
|
|
// The check for MainKeyboardView ensures that a red border is only placed around
|
2012-07-19 02:21:54 +00:00
|
|
|
// the main keyboard, not every keyboard.
|
2012-07-23 01:27:14 +00:00
|
|
|
if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
|
2012-07-19 02:21:54 +00:00
|
|
|
final int savedColor = paint.getColor();
|
|
|
|
paint.setColor(Color.RED);
|
|
|
|
final Style savedStyle = paint.getStyle();
|
|
|
|
paint.setStyle(Style.STROKE);
|
|
|
|
final float savedStrokeWidth = paint.getStrokeWidth();
|
|
|
|
if (IS_SHOWING_INDICATOR_CLEARLY) {
|
|
|
|
paint.setStrokeWidth(5);
|
2012-08-15 00:04:48 +00:00
|
|
|
canvas.drawLine(0, 0, 0, height, paint);
|
|
|
|
canvas.drawLine(width, 0, width, height, paint);
|
2012-07-19 02:21:54 +00:00
|
|
|
} 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,
|
|
|
|
// placed at the lower-right corner of the canvas, painted with a non-zero border
|
|
|
|
// width.
|
|
|
|
paint.setStrokeWidth(3);
|
|
|
|
canvas.drawRect(width, height, width, height, paint);
|
2012-07-18 20:52:41 +00:00
|
|
|
}
|
2012-07-19 02:21:54 +00:00
|
|
|
paint.setColor(savedColor);
|
|
|
|
paint.setStyle(savedStyle);
|
|
|
|
paint.setStrokeWidth(savedStrokeWidth);
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
2012-06-13 23:37:20 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
/**
|
2012-06-16 00:49:42 +00:00
|
|
|
* Buffer a research log event, flagging it as privacy-sensitive.
|
2012-05-14 16:41:39 +00:00
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private synchronized void enqueueEvent(LogStatement logStatement, Object... values) {
|
|
|
|
assert values.length == logStatement.mKeys.length;
|
2012-06-29 14:02:39 +00:00
|
|
|
if (isAllowedToLog()) {
|
2012-08-09 22:58:25 +00:00
|
|
|
final long time = SystemClock.uptimeMillis();
|
2012-08-10 21:21:18 +00:00
|
|
|
mCurrentLogUnit.addLogStatement(logStatement, time, values);
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-08-03 03:22:29 +00:00
|
|
|
private void setCurrentLogUnitContainsDigitFlag() {
|
2012-08-09 22:58:25 +00:00
|
|
|
mCurrentLogUnit.setMayContainDigit();
|
2012-08-03 03:22:29 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 08:54:06 +00:00
|
|
|
/* package for test */ void commitCurrentLogUnit() {
|
2012-08-09 03:13:41 +00:00
|
|
|
if (DEBUG) {
|
2012-08-09 19:20:45 +00:00
|
|
|
Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ?
|
|
|
|
": " + mCurrentLogUnit.getWord() : ""));
|
2012-08-09 03:13:41 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
if (!mCurrentLogUnit.isEmpty()) {
|
|
|
|
if (mMainLogBuffer != null) {
|
|
|
|
mMainLogBuffer.shiftIn(mCurrentLogUnit);
|
2012-12-18 21:57:22 +00:00
|
|
|
if ((mMainLogBuffer.isSafeToLog() || LOG_EVERYTHING) && mMainResearchLog != null) {
|
2012-08-03 03:22:29 +00:00
|
|
|
publishLogBuffer(mMainLogBuffer, mMainResearchLog,
|
|
|
|
true /* isIncludingPrivateData */);
|
|
|
|
mMainLogBuffer.resetWordCounter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mFeedbackLogBuffer != null) {
|
|
|
|
mFeedbackLogBuffer.shiftIn(mCurrentLogUnit);
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
2012-08-10 08:54:06 +00:00
|
|
|
mCurrentLogUnit = new LogUnit();
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
}
|
2012-06-21 03:23:19 +00:00
|
|
|
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_OPENING =
|
|
|
|
new LogStatement("logSegmentStart", false, false, "isIncludingPrivateData");
|
|
|
|
private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_CLOSING =
|
|
|
|
new LogStatement("logSegmentEnd", false, false);
|
2012-08-03 03:22:29 +00:00
|
|
|
/* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
|
|
|
|
final ResearchLog researchLog, final boolean isIncludingPrivateData) {
|
2012-08-09 19:24:24 +00:00
|
|
|
final LogUnit openingLogUnit = new LogUnit();
|
2012-08-24 03:49:07 +00:00
|
|
|
if (logBuffer.isEmpty()) return;
|
2012-08-10 21:21:18 +00:00
|
|
|
openingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_OPENING, SystemClock.uptimeMillis(),
|
|
|
|
isIncludingPrivateData);
|
2012-08-09 19:24:24 +00:00
|
|
|
researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */);
|
2012-08-03 03:22:29 +00:00
|
|
|
LogUnit logUnit;
|
|
|
|
while ((logUnit = logBuffer.shiftOut()) != null) {
|
2012-08-10 08:54:06 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "publishLogBuffer: " + (logUnit.hasWord() ? logUnit.getWord()
|
|
|
|
: "<wordless>"));
|
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
researchLog.publish(logUnit, isIncludingPrivateData);
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
2012-08-09 19:24:24 +00:00
|
|
|
final LogUnit closingLogUnit = new LogUnit();
|
2012-08-10 21:21:18 +00:00
|
|
|
closingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_CLOSING,
|
2012-08-10 08:54:06 +00:00
|
|
|
SystemClock.uptimeMillis());
|
2012-08-09 19:24:24 +00:00
|
|
|
researchLog.publish(closingLogUnit, true /* isIncludingPrivateData */);
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 08:54:06 +00:00
|
|
|
public static boolean hasLetters(final String word) {
|
2012-06-19 00:48:00 +00:00
|
|
|
final int length = word.length();
|
|
|
|
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
|
2012-08-03 03:22:29 +00:00
|
|
|
final int codePoint = word.codePointAt(i);
|
2012-08-09 19:24:47 +00:00
|
|
|
if (Character.isLetter(codePoint)) {
|
|
|
|
return true;
|
2012-06-19 00:48:00 +00:00
|
|
|
}
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
2012-08-09 19:24:47 +00:00
|
|
|
return false;
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 02:09:28 +00:00
|
|
|
/**
|
|
|
|
* Commit the portion of mCurrentLogUnit before maxTime as a worded logUnit.
|
|
|
|
*
|
|
|
|
* After this operation completes, mCurrentLogUnit will hold any logStatements that happened
|
|
|
|
* after maxTime.
|
|
|
|
*/
|
2012-08-10 21:21:18 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS =
|
|
|
|
new LogStatement("recordSplitWords", true, false);
|
2012-08-31 02:09:28 +00:00
|
|
|
/* package for test */ void commitCurrentLogUnitAsWord(final String word, final long maxTime) {
|
2012-08-13 15:24:40 +00:00
|
|
|
final Dictionary dictionary = getDictionary();
|
2012-08-09 19:24:47 +00:00
|
|
|
if (word != null && word.length() > 0 && hasLetters(word)) {
|
2012-08-03 03:22:29 +00:00
|
|
|
mCurrentLogUnit.setWord(word);
|
2012-08-13 15:24:40 +00:00
|
|
|
final boolean isDictionaryWord = dictionary != null
|
|
|
|
&& dictionary.isValidWord(word);
|
2012-08-10 21:21:18 +00:00
|
|
|
mStatistics.recordWordEntered(isDictionaryWord);
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
2012-08-10 08:54:06 +00:00
|
|
|
final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
|
|
|
|
enqueueCommitText(word);
|
|
|
|
commitCurrentLogUnit();
|
|
|
|
mCurrentLogUnit = newLogUnit;
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 02:09:28 +00:00
|
|
|
public void onWordFinished(final String word) {
|
|
|
|
commitCurrentLogUnitAsWord(word, mSavedDownEventTime);
|
|
|
|
mSavedDownEventTime = Long.MAX_VALUE;
|
|
|
|
}
|
|
|
|
|
2012-06-15 05:15:27 +00:00
|
|
|
private static int scrubDigitFromCodePoint(int codePoint) {
|
|
|
|
return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* package for test */ static String scrubDigitsFromString(String s) {
|
|
|
|
StringBuilder sb = null;
|
|
|
|
final int length = s.length();
|
|
|
|
for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) {
|
2012-06-19 00:48:00 +00:00
|
|
|
final int codePoint = Character.codePointAt(s, i);
|
2012-06-15 05:15:27 +00:00
|
|
|
if (Character.isDigit(codePoint)) {
|
|
|
|
if (sb == null) {
|
|
|
|
sb = new StringBuilder(length);
|
|
|
|
sb.append(s.substring(0, i));
|
|
|
|
}
|
|
|
|
sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT);
|
|
|
|
} else {
|
|
|
|
if (sb != null) {
|
|
|
|
sb.appendCodePoint(codePoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sb == null) {
|
|
|
|
return s;
|
|
|
|
} else {
|
|
|
|
return sb.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-06-16 00:49:42 +00:00
|
|
|
private String scrubWord(String word) {
|
2012-08-13 15:24:40 +00:00
|
|
|
final Dictionary dictionary = getDictionary();
|
|
|
|
if (dictionary == null) {
|
2012-06-16 00:49:42 +00:00
|
|
|
return WORD_REPLACEMENT_STRING;
|
|
|
|
}
|
2012-08-13 15:24:40 +00:00
|
|
|
if (dictionary.isValidWord(word)) {
|
2012-06-16 00:49:42 +00:00
|
|
|
return word;
|
|
|
|
}
|
|
|
|
return WORD_REPLACEMENT_STRING;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
// Specific logging methods follow below. The comments for each logging method should
|
|
|
|
// indicate what specific method is logged, and how to trigger it from the user interface.
|
|
|
|
//
|
|
|
|
// Logging methods can be generally classified into two flavors, "UserAction", which should
|
|
|
|
// correspond closely to an event that is sensed by the IME, and is usually generated
|
|
|
|
// directly by the user, and "SystemResponse" which corresponds to an event that the IME
|
|
|
|
// generates, often after much processing of user input. SystemResponses should correspond
|
|
|
|
// closely to user-visible events.
|
|
|
|
// TODO: Consider exposing the UserAction classification in the log output.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onStartInputViewInternal().
|
|
|
|
*
|
|
|
|
* UserAction: called each time the keyboard is opened up.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL =
|
|
|
|
new LogStatement("LatinImeOnStartInputViewInternal", false, false, "uuid",
|
|
|
|
"packageName", "inputType", "imeOptions", "fieldId", "display", "model",
|
2012-12-18 02:19:58 +00:00
|
|
|
"prefs", "versionCode", "versionName", "outputFormatVersion", "logEverything",
|
|
|
|
"isExperimentalDebug");
|
2012-07-18 20:52:41 +00:00
|
|
|
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
|
|
|
|
final SharedPreferences prefs) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
if (editorInfo != null) {
|
2012-08-09 03:28:48 +00:00
|
|
|
final boolean isPassword = InputTypeUtils.isPasswordInputType(editorInfo.inputType)
|
|
|
|
|| InputTypeUtils.isVisiblePasswordInputType(editorInfo.inputType);
|
|
|
|
getInstance().setIsPasswordView(isPassword);
|
|
|
|
researchLogger.start();
|
2012-12-22 22:18:16 +00:00
|
|
|
final Context context = researchLogger.mLatinIME;
|
2012-07-18 20:52:41 +00:00
|
|
|
try {
|
|
|
|
final PackageInfo packageInfo;
|
|
|
|
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
|
|
|
0);
|
|
|
|
final Integer versionCode = packageInfo.versionCode;
|
|
|
|
final String versionName = packageInfo.versionName;
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL,
|
2012-07-18 20:52:41 +00:00
|
|
|
researchLogger.mUUIDString, editorInfo.packageName,
|
|
|
|
Integer.toHexString(editorInfo.inputType),
|
|
|
|
Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
|
|
|
|
Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
|
2012-12-18 02:19:58 +00:00
|
|
|
OUTPUT_FORMAT_VERSION, LOG_EVERYTHING,
|
|
|
|
ProductionFlag.IS_EXPERIMENTAL_DEBUG);
|
2012-07-18 20:52:41 +00:00
|
|
|
} catch (NameNotFoundException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-13 02:28:31 +00:00
|
|
|
public void latinIME_onFinishInputViewInternal() {
|
2012-08-24 03:49:34 +00:00
|
|
|
logStatistics();
|
2012-07-18 20:52:41 +00:00
|
|
|
stop();
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a change in preferences.
|
|
|
|
*
|
|
|
|
* UserAction: called when the user changes the settings.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_PREFS_CHANGED =
|
|
|
|
new LogStatement("PrefsChanged", false, false, "prefs");
|
2012-08-07 01:29:51 +00:00
|
|
|
public static void prefsChanged(final SharedPreferences prefs) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_PREFS_CHANGED, prefs);
|
2012-08-07 01:29:51 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to MainKeyboardView.processMotionEvent().
|
|
|
|
*
|
|
|
|
* UserAction: called when the user puts their finger onto the screen (ACTION_DOWN).
|
|
|
|
*
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
|
|
|
|
new LogStatement("MainKeyboardViewProcessMotionEvent", true, false, "action",
|
|
|
|
"eventTime", "id", "x", "y", "size", "pressure");
|
2012-07-23 01:27:14 +00:00
|
|
|
public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
|
2012-05-14 16:41:39 +00:00
|
|
|
final long eventTime, final int index, final int id, final int x, final int y) {
|
|
|
|
if (me != null) {
|
|
|
|
final String actionString;
|
|
|
|
switch (action) {
|
|
|
|
case MotionEvent.ACTION_CANCEL: actionString = "CANCEL"; break;
|
|
|
|
case MotionEvent.ACTION_UP: actionString = "UP"; break;
|
|
|
|
case MotionEvent.ACTION_DOWN: actionString = "DOWN"; break;
|
|
|
|
case MotionEvent.ACTION_POINTER_UP: actionString = "POINTER_UP"; break;
|
|
|
|
case MotionEvent.ACTION_POINTER_DOWN: actionString = "POINTER_DOWN"; break;
|
|
|
|
case MotionEvent.ACTION_MOVE: actionString = "MOVE"; break;
|
|
|
|
case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break;
|
|
|
|
default: actionString = "ACTION_" + action; break;
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
final float size = me.getSize(index);
|
|
|
|
final float pressure = me.getPressure(index);
|
2012-08-31 02:09:28 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
|
2012-08-10 08:54:06 +00:00
|
|
|
actionString, eventTime, id, x, y, size, pressure);
|
2012-08-31 02:09:28 +00:00
|
|
|
if (action == MotionEvent.ACTION_DOWN) {
|
|
|
|
// Subtract 1 from eventTime so the down event is included in the later
|
|
|
|
// LogUnit, not the earlier (the test is for inequality).
|
|
|
|
researchLogger.mSavedDownEventTime = eventTime - 1;
|
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onCodeInput().
|
|
|
|
*
|
|
|
|
* SystemResponse: The main processing step for entering text. Called when the user performs a
|
|
|
|
* tap, a flick, a long press, releases a gesture, or taps a punctuation suggestion.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT =
|
|
|
|
new LogStatement("LatinImeOnCodeInput", true, false, "code", "x", "y");
|
2012-05-14 16:41:39 +00:00
|
|
|
public static void latinIME_onCodeInput(final int code, final int x, final int y) {
|
2012-08-03 03:22:29 +00:00
|
|
|
final long time = SystemClock.uptimeMillis();
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT,
|
|
|
|
Constants.printableCode(scrubDigitFromCodePoint(code)), x, y);
|
2012-08-03 03:22:29 +00:00
|
|
|
if (Character.isDigit(code)) {
|
|
|
|
researchLogger.setCurrentLogUnitContainsDigitFlag();
|
|
|
|
}
|
|
|
|
researchLogger.mStatistics.recordChar(code, time);
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onDisplayCompletions().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has displayed application-specific completions. They may show up
|
|
|
|
* in the suggestion strip, such as a landscape phone.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS =
|
|
|
|
new LogStatement("LatinIMEOnDisplayCompletions", true, true,
|
|
|
|
"applicationSpecifiedCompletions");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void latinIME_onDisplayCompletions(
|
|
|
|
final CompletionInfo[] applicationSpecifiedCompletions) {
|
2012-08-10 08:54:06 +00:00
|
|
|
// Note; passing an array as a single element in a vararg list. Must create a new
|
|
|
|
// dummy array around it or it will get expanded.
|
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS,
|
|
|
|
new Object[] { applicationSpecifiedCompletions });
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-07-20 18:02:39 +00:00
|
|
|
public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
|
2012-05-14 16:41:39 +00:00
|
|
|
boolean returnValue = sLatinIMEExpectingUpdateSelection;
|
|
|
|
sLatinIMEExpectingUpdateSelection = false;
|
2012-05-02 05:09:53 +00:00
|
|
|
return returnValue;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onWindowHidden().
|
|
|
|
*
|
|
|
|
* UserAction: The user has performed an action that has caused the IME to be closed. They may
|
|
|
|
* have focused on something other than a text field, or explicitly closed it.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN =
|
|
|
|
new LogStatement("LatinIMEOnWindowHidden", false, false, "isTextTruncated", "text");
|
2012-05-02 05:09:53 +00:00
|
|
|
public static void latinIME_onWindowHidden(final int savedSelectionStart,
|
|
|
|
final int savedSelectionEnd, final InputConnection ic) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (ic != null) {
|
2012-08-10 08:54:06 +00:00
|
|
|
final boolean isTextTruncated;
|
|
|
|
final String text;
|
2012-12-22 21:57:58 +00:00
|
|
|
if (LOG_FULL_TEXTVIEW_CONTENTS) {
|
2012-12-03 19:46:25 +00:00
|
|
|
// 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
|
|
|
|
// therefore keep user-visible state as is.
|
|
|
|
ic.beginBatchEdit();
|
|
|
|
ic.performContextMenuAction(android.R.id.selectAll);
|
|
|
|
CharSequence charSequence = ic.getSelectedText(0);
|
|
|
|
if (savedSelectionStart != -1 && savedSelectionEnd != -1) {
|
|
|
|
ic.setSelection(savedSelectionStart, savedSelectionEnd);
|
|
|
|
}
|
|
|
|
ic.endBatchEdit();
|
|
|
|
sLatinIMEExpectingUpdateSelection = true;
|
2012-06-16 00:49:42 +00:00
|
|
|
if (TextUtils.isEmpty(charSequence)) {
|
2012-08-10 08:54:06 +00:00
|
|
|
isTextTruncated = false;
|
|
|
|
text = "";
|
2012-06-16 00:49:42 +00:00
|
|
|
} else {
|
|
|
|
if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) {
|
|
|
|
int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE;
|
|
|
|
// do not cut in the middle of a supplementary character
|
|
|
|
final char c = charSequence.charAt(length - 1);
|
|
|
|
if (Character.isHighSurrogate(c)) {
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
final CharSequence truncatedCharSequence = charSequence.subSequence(0,
|
|
|
|
length);
|
2012-08-10 08:54:06 +00:00
|
|
|
isTextTruncated = true;
|
|
|
|
text = truncatedCharSequence.toString();
|
2012-06-16 00:49:42 +00:00
|
|
|
} else {
|
2012-08-10 08:54:06 +00:00
|
|
|
isTextTruncated = false;
|
|
|
|
text = charSequence.toString();
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
} else {
|
2012-08-10 08:54:06 +00:00
|
|
|
isTextTruncated = true;
|
|
|
|
text = "";
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
// Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g.
|
|
|
|
// during a live user test), so the normal isPotentiallyPrivate and
|
|
|
|
// isPotentiallyRevealing flags do not apply
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONWINDOWHIDDEN, isTextTruncated,
|
|
|
|
text);
|
|
|
|
researchLogger.commitCurrentLogUnit();
|
2012-07-19 01:41:15 +00:00
|
|
|
getInstance().stop();
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onUpdateSelection().
|
|
|
|
*
|
|
|
|
* UserAction/SystemResponse: The user has moved the cursor or selection. This function may
|
|
|
|
* be called, however, when the system has moved the cursor, say by inserting a character.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_ONUPDATESELECTION =
|
|
|
|
new LogStatement("LatinIMEOnUpdateSelection", true, false, "lastSelectionStart",
|
|
|
|
"lastSelectionEnd", "oldSelStart", "oldSelEnd", "newSelStart", "newSelEnd",
|
|
|
|
"composingSpanStart", "composingSpanEnd", "expectingUpdateSelection",
|
|
|
|
"expectingUpdateSelectionFromLogger", "context");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void latinIME_onUpdateSelection(final int lastSelectionStart,
|
|
|
|
final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
|
|
|
|
final int newSelStart, final int newSelEnd, final int composingSpanStart,
|
2012-05-02 05:09:53 +00:00
|
|
|
final int composingSpanEnd, final boolean expectingUpdateSelection,
|
2012-06-08 10:44:38 +00:00
|
|
|
final boolean expectingUpdateSelectionFromLogger,
|
|
|
|
final RichInputConnection connection) {
|
2012-06-12 17:56:03 +00:00
|
|
|
String word = "";
|
|
|
|
if (connection != null) {
|
|
|
|
Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1);
|
|
|
|
if (range != null) {
|
|
|
|
word = range.mWord;
|
|
|
|
}
|
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
final String scrubbedWord = researchLogger.scrubWord(word);
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONUPDATESELECTION, lastSelectionStart,
|
|
|
|
lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd,
|
|
|
|
composingSpanStart, composingSpanEnd, expectingUpdateSelection,
|
|
|
|
expectingUpdateSelectionFromLogger, scrubbedWord);
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 02:09:28 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onTextInput().
|
|
|
|
*
|
|
|
|
* SystemResponse: Raw text is added to the TextView.
|
|
|
|
*/
|
|
|
|
public static void latinIME_onTextInput(final String text) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE);
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.pickSuggestionManually().
|
|
|
|
*
|
|
|
|
* UserAction: The user has chosen a specific word from the suggestion strip.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY =
|
|
|
|
new LogStatement("LatinIMEPickSuggestionManually", true, false, "replacedWord", "index",
|
|
|
|
"suggestion", "x", "y");
|
2012-04-12 04:42:22 +00:00
|
|
|
public static void latinIME_pickSuggestionManually(final String replacedWord,
|
2012-08-28 17:26:21 +00:00
|
|
|
final int index, final String suggestion) {
|
|
|
|
final String scrubbedWord = scrubDigitsFromString(suggestion);
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY,
|
|
|
|
scrubDigitsFromString(replacedWord), index,
|
2012-08-28 17:26:21 +00:00
|
|
|
suggestion == null ? null : scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE,
|
|
|
|
Constants.SUGGESTION_STRIP_COORDINATE);
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE);
|
2012-08-28 17:26:21 +00:00
|
|
|
researchLogger.mStatistics.recordManualSuggestion();
|
2012-04-12 04:42:22 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.punctuationSuggestion().
|
|
|
|
*
|
|
|
|
* UserAction: The user has chosen punctuation from the punctuation suggestion strip.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION =
|
|
|
|
new LogStatement("LatinIMEPunctuationSuggestion", false, false, "index", "suggestion",
|
|
|
|
"x", "y");
|
2012-08-28 17:26:21 +00:00
|
|
|
public static void latinIME_punctuationSuggestion(final int index, final String suggestion) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
|
2012-08-10 08:54:06 +00:00
|
|
|
Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(suggestion, Long.MAX_VALUE);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.sendKeyCodePoint().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is simulating a hardware keypress. This happens for numbers; other
|
|
|
|
* input typically goes through RichInputConnection.setComposingText() and
|
|
|
|
* RichInputConnection.commitText().
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT =
|
|
|
|
new LogStatement("LatinIMESendKeyCodePoint", true, false, "code");
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_sendKeyCodePoint(final int code) {
|
2012-08-03 03:22:29 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT,
|
|
|
|
Constants.printableCode(scrubDigitFromCodePoint(code)));
|
2012-08-03 03:22:29 +00:00
|
|
|
if (Character.isDigit(code)) {
|
|
|
|
researchLogger.setCurrentLogUnitContainsDigitFlag();
|
|
|
|
}
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.swapSwapperAndSpace().
|
|
|
|
*
|
|
|
|
* SystemResponse: A symbol has been swapped with a space character. E.g. punctuation may swap
|
|
|
|
* if a soft space is inserted after a word.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE =
|
|
|
|
new LogStatement("LatinIMESwapSwapperAndSpace", false, false);
|
2012-08-31 02:09:28 +00:00
|
|
|
public static void latinIME_swapSwapperAndSpace(final String text) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE);
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.maybeDoubleSpacePeriod().
|
|
|
|
*
|
|
|
|
* SystemResponse: Two spaces have been replaced by period space.
|
|
|
|
*/
|
|
|
|
public static void latinIME_maybeDoubleSpacePeriod(final String text) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE);
|
2012-04-12 04:42:22 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to MainKeyboardView.onLongPress().
|
|
|
|
*
|
|
|
|
* UserAction: The user has performed a long-press on a key.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS =
|
|
|
|
new LogStatement("MainKeyboardViewOnLongPress", false, false);
|
2012-07-23 01:27:14 +00:00
|
|
|
public static void mainKeyboardView_onLongPress() {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to MainKeyboardView.setKeyboard().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has switched to a new keyboard (e.g. French, English).
|
|
|
|
* This is typically called right after LatinIME.onStartInputViewInternal (when starting a new
|
|
|
|
* IME), but may happen at other times if the user explicitly requests a keyboard change.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD =
|
|
|
|
new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale",
|
|
|
|
"orientation", "width", "modeName", "action", "navigateNext",
|
|
|
|
"navigatePrevious", "clobberSettingsKey", "passwordInput", "shortcutKeyEnabled",
|
|
|
|
"hasShortcutKey", "languageSwitchKeyEnabled", "isMultiLine", "tw", "th",
|
|
|
|
"keys");
|
2012-07-23 01:27:14 +00:00
|
|
|
public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) {
|
2012-08-10 08:54:06 +00:00
|
|
|
final KeyboardId kid = keyboard.mId;
|
|
|
|
final boolean isPasswordView = kid.passwordInput();
|
|
|
|
getInstance().setIsPasswordView(isPasswordView);
|
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD,
|
2012-08-09 02:49:15 +00:00
|
|
|
KeyboardId.elementIdToName(kid.mElementId),
|
|
|
|
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
|
2012-08-10 08:54:06 +00:00
|
|
|
kid.mOrientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(),
|
|
|
|
kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey,
|
|
|
|
isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey,
|
|
|
|
kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth,
|
|
|
|
keyboard.mOccupiedHeight, keyboard.mKeys);
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.revertCommit().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has reverted commited text. This happens when the user enters
|
|
|
|
* a word, commits it by pressing space or punctuation, and then reverts the commit by hitting
|
|
|
|
* backspace.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_REVERTCOMMIT =
|
2012-08-28 17:26:21 +00:00
|
|
|
new LogStatement("LatinIMERevertCommit", true, false, "committedWord",
|
|
|
|
"originallyTypedWord");
|
|
|
|
public static void latinIME_revertCommit(final String committedWord,
|
|
|
|
final String originallyTypedWord) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord,
|
|
|
|
originallyTypedWord);
|
|
|
|
researchLogger.mStatistics.recordRevertCommit();
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(originallyTypedWord, Long.MAX_VALUE);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to PointerTracker.callListenerOnCancelInput().
|
|
|
|
*
|
|
|
|
* UserAction: The user has canceled the input, e.g., by pressing down, but then removing
|
|
|
|
* outside the keyboard area.
|
|
|
|
* TODO: Verify
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT =
|
|
|
|
new LogStatement("PointerTrackerCallListenerOnCancelInput", false, false);
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_callListenerOnCancelInput() {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to PointerTracker.callListenerOnCodeInput().
|
|
|
|
*
|
|
|
|
* SystemResponse: The user has entered a key through the normal tapping mechanism.
|
|
|
|
* LatinIME.onCodeInput will also be called.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT =
|
|
|
|
new LogStatement("PointerTrackerCallListenerOnCodeInput", true, false, "code",
|
|
|
|
"outputText", "x", "y", "ignoreModifierKey", "altersCode", "isEnabled");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
|
|
|
|
final int y, final boolean ignoreModifierKey, final boolean altersCode,
|
|
|
|
final int code) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (key != null) {
|
2012-08-28 08:19:49 +00:00
|
|
|
String outputText = key.getOutputText();
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT,
|
|
|
|
Constants.printableCode(scrubDigitFromCodePoint(code)),
|
|
|
|
outputText == null ? null : scrubDigitsFromString(outputText.toString()),
|
|
|
|
x, y, ignoreModifierKey, altersCode, key.isEnabled());
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to PointerTracker.callListenerCallListenerOnRelease().
|
|
|
|
*
|
|
|
|
* UserAction: The user has released their finger or thumb from the screen.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE =
|
|
|
|
new LogStatement("PointerTrackerCallListenerOnRelease", true, false, "code",
|
|
|
|
"withSliding", "ignoreModifierKey", "isEnabled");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
|
|
|
|
final boolean withSliding, final boolean ignoreModifierKey) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (key != null) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE,
|
|
|
|
Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
|
|
|
|
ignoreModifierKey, key.isEnabled());
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to PointerTracker.onDownEvent().
|
|
|
|
*
|
|
|
|
* UserAction: The user has pressed down on a key.
|
|
|
|
* TODO: Differentiate with LatinIME.processMotionEvent.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT =
|
|
|
|
new LogStatement("PointerTrackerOnDownEvent", true, false, "deltaT", "distanceSquared");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT, deltaT,
|
|
|
|
distanceSquared);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to PointerTracker.onMoveEvent().
|
|
|
|
*
|
|
|
|
* UserAction: The user has moved their finger while pressing on the screen.
|
|
|
|
* TODO: Differentiate with LatinIME.processMotionEvent().
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT =
|
|
|
|
new LogStatement("PointerTrackerOnMoveEvent", true, false, "x", "y", "lastX", "lastY");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
|
|
|
|
final int lastY) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT, x, y, lastX, lastY);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.commitCompletion().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has committed a completion. A completion is an application-
|
|
|
|
* specific suggestion that is presented in a pop-up menu in the TextView.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION =
|
|
|
|
new LogStatement("RichInputConnectionCommitCompletion", true, false, "completionInfo");
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION,
|
|
|
|
completionInfo);
|
|
|
|
}
|
|
|
|
|
2012-08-31 02:09:28 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.revertDoubleSpacePeriod().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has reverted ". ", which had previously replaced two typed spaces.
|
|
|
|
*/
|
|
|
|
public static void richInputConnection_revertDoubleSpacePeriod(final String doubleSpace) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.commitCurrentLogUnitAsWord(doubleSpace, Long.MAX_VALUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.revertSwapPunctuation().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has reverted a punctuation swap.
|
|
|
|
*/
|
|
|
|
public static void richInputConnection_revertSwapPunctuation(final String text) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE);
|
|
|
|
}
|
|
|
|
|
2012-12-22 21:34:59 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.commitCurrentAutoCorrection().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has committed an auto-correction. An auto-correction changes the raw
|
|
|
|
* text input to another word that the user more likely desired to type.
|
|
|
|
*/
|
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION =
|
|
|
|
new LogStatement("LatinIMECommitCurrentAutoCorrection", true, false, "typedWord",
|
|
|
|
"autoCorrection", "separatorString");
|
|
|
|
public static void latinIme_commitCurrentAutoCorrection(final String typedWord,
|
|
|
|
final String autoCorrection, final String separatorString) {
|
|
|
|
final String scrubbedTypedWord = scrubDigitsFromString(typedWord);
|
|
|
|
final String scrubbedAutoCorrection = scrubDigitsFromString(autoCorrection);
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION,
|
|
|
|
scrubbedTypedWord, scrubbedAutoCorrection, separatorString);
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(scrubbedAutoCorrection, Long.MAX_VALUE);
|
2012-12-22 21:34:59 +00:00
|
|
|
}
|
|
|
|
|
2012-08-10 08:54:06 +00:00
|
|
|
private boolean isExpectingCommitText = false;
|
2012-08-28 17:26:21 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.commitPartialText
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is committing part of a word. This happens if a space is
|
|
|
|
* automatically inserted to split a single typed string into two or more words.
|
|
|
|
*/
|
|
|
|
// TODO: This method is currently unused. Find where it should be called from in the IME and
|
|
|
|
// add invocations.
|
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT =
|
|
|
|
new LogStatement("LatinIMECommitPartialText", true, false, "newCursorPosition");
|
2012-08-10 08:54:06 +00:00
|
|
|
public static void latinIME_commitPartialText(final CharSequence committedWord,
|
|
|
|
final long lastTimestampOfWordData) {
|
2012-08-02 00:26:45 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
|
2012-08-28 17:26:21 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT);
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, lastTimestampOfWordData);
|
2012-08-28 17:26:21 +00:00
|
|
|
researchLogger.mStatistics.recordSplitWords();
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.commitText().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is committing text. This happens after the user has typed a word
|
|
|
|
* and then a space or punctuation key.
|
|
|
|
*/
|
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT =
|
|
|
|
new LogStatement("RichInputConnectionCommitText", true, false, "newCursorPosition");
|
2012-08-10 08:54:06 +00:00
|
|
|
public static void richInputConnection_commitText(final CharSequence committedWord,
|
2012-08-02 00:26:45 +00:00
|
|
|
final int newCursorPosition) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
2012-08-10 08:54:06 +00:00
|
|
|
final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
|
|
|
|
if (!researchLogger.isExpectingCommitText) {
|
2012-08-23 01:59:31 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT,
|
|
|
|
newCursorPosition);
|
2012-08-31 02:09:28 +00:00
|
|
|
researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE);
|
2012-08-10 08:54:06 +00:00
|
|
|
}
|
|
|
|
researchLogger.isExpectingCommitText = false;
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Shared event for logging committed text.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_COMMITTEXT =
|
|
|
|
new LogStatement("CommitText", true, false, "committedText");
|
|
|
|
private void enqueueCommitText(final CharSequence word) {
|
|
|
|
enqueueEvent(LOGSTATEMENT_COMMITTEXT, word);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.deleteSurroundingText().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has deleted text.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT =
|
|
|
|
new LogStatement("RichInputConnectionDeleteSurroundingText", true, false,
|
|
|
|
"beforeLength", "afterLength");
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_deleteSurroundingText(final int beforeLength,
|
|
|
|
final int afterLength) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT,
|
|
|
|
beforeLength, afterLength);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.finishComposingText().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has left the composing text as-is.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT =
|
|
|
|
new LogStatement("RichInputConnectionFinishComposingText", false, false);
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_finishComposingText() {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.performEditorAction().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is invoking an action specific to the editor.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION =
|
|
|
|
new LogStatement("RichInputConnectionPerformEditorAction", false, false,
|
2012-08-23 01:59:31 +00:00
|
|
|
"imeActionId");
|
|
|
|
public static void richInputConnection_performEditorAction(final int imeActionId) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION,
|
2012-08-23 01:59:31 +00:00
|
|
|
imeActionId);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.sendKeyEvent().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is telling the TextView that a key is being pressed through an
|
|
|
|
* alternate channel.
|
|
|
|
* TODO: only for hardware keys?
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT =
|
|
|
|
new LogStatement("RichInputConnectionSendKeyEvent", true, false, "eventTime", "action",
|
|
|
|
"code");
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT,
|
|
|
|
keyEvent.getEventTime(), keyEvent.getAction(), keyEvent.getKeyCode());
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.setComposingText().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is setting the composing text. Happens each time a character is
|
|
|
|
* entered.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT =
|
|
|
|
new LogStatement("RichInputConnectionSetComposingText", true, true, "text",
|
|
|
|
"newCursorPosition");
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_setComposingText(final CharSequence text,
|
|
|
|
final int newCursorPosition) {
|
2012-08-03 03:22:29 +00:00
|
|
|
if (text == null) {
|
|
|
|
throw new RuntimeException("setComposingText is null");
|
|
|
|
}
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, text,
|
|
|
|
newCursorPosition);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to RichInputConnection.setSelection().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is requesting that the selection change. User-initiated selection-
|
|
|
|
* change requests do not go through this method -- it's only when the system wants to change
|
|
|
|
* the selection.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION =
|
|
|
|
new LogStatement("RichInputConnectionSetSelection", true, false, "from", "to");
|
2012-08-02 00:26:45 +00:00
|
|
|
public static void richInputConnection_setSelection(final int from, final int to) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION, from, to);
|
2012-08-02 00:26:45 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to SuddenJumpingTouchEventHandler.onTouchEvent().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME has filtered input events in case of an erroneous sensor reading.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT =
|
|
|
|
new LogStatement("SuddenJumpingTouchEventHandlerOnTouchEvent", true, false,
|
|
|
|
"motionEvent");
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (me != null) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT,
|
|
|
|
me.toString());
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to SuggestionsView.setSuggestions().
|
|
|
|
*
|
|
|
|
* SystemResponse: The IME is setting the suggestions in the suggestion strip.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS =
|
|
|
|
new LogStatement("SuggestionStripViewSetSuggestions", true, true, "suggestedWords");
|
2012-07-23 06:28:28 +00:00
|
|
|
public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (suggestedWords != null) {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS,
|
|
|
|
suggestedWords);
|
2012-04-09 17:53:17 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-04 19:27:37 +00:00
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* The user has indicated a particular point in the log that is of interest.
|
|
|
|
*
|
|
|
|
* UserAction: From direct menu invocation.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_USER_TIMESTAMP =
|
|
|
|
new LogStatement("UserTimestamp", false, false);
|
2012-06-04 19:27:37 +00:00
|
|
|
public void userTimestamp() {
|
2012-08-10 08:54:06 +00:00
|
|
|
getInstance().enqueueEvent(LOGSTATEMENT_USER_TIMESTAMP);
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
2012-08-03 03:22:29 +00:00
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.onEndBatchInput().
|
|
|
|
*
|
|
|
|
* SystemResponse: The system has completed a gesture.
|
|
|
|
*/
|
2012-08-10 21:21:18 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_ONENDBATCHINPUT =
|
|
|
|
new LogStatement("LatinIMEOnEndBatchInput", true, false, "enteredText",
|
|
|
|
"enteredWordPos");
|
|
|
|
public static void latinIME_onEndBatchInput(final CharSequence enteredText,
|
|
|
|
final int enteredWordPos) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText,
|
|
|
|
enteredWordPos);
|
2012-08-13 17:30:12 +00:00
|
|
|
researchLogger.mStatistics.recordGestureInput(enteredText.length());
|
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log a call to LatinIME.handleBackspace().
|
|
|
|
*
|
|
|
|
* UserInput: The user is deleting a gestured word by hitting the backspace key once.
|
|
|
|
*/
|
2012-08-13 17:30:12 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH =
|
|
|
|
new LogStatement("LatinIMEHandleBackspaceBatch", true, false, "deletedText");
|
|
|
|
public static void latinIME_handleBackspace_batch(final CharSequence deletedText) {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH, deletedText);
|
|
|
|
researchLogger.mStatistics.recordGestureDelete();
|
2012-08-10 21:21:18 +00:00
|
|
|
}
|
|
|
|
|
2012-08-23 01:59:31 +00:00
|
|
|
/**
|
|
|
|
* Log statistics.
|
|
|
|
*
|
|
|
|
* ContextualData, recorded at the end of a session.
|
|
|
|
*/
|
2012-08-10 08:54:06 +00:00
|
|
|
private static final LogStatement LOGSTATEMENT_STATISTICS =
|
|
|
|
new LogStatement("Statistics", false, false, "charCount", "letterCount", "numberCount",
|
|
|
|
"spaceCount", "deleteOpsCount", "wordCount", "isEmptyUponStarting",
|
|
|
|
"isEmptinessStateKnown", "averageTimeBetweenKeys", "averageTimeBeforeDelete",
|
2012-08-10 21:21:18 +00:00
|
|
|
"averageTimeDuringRepeatedDelete", "averageTimeAfterDelete",
|
2012-08-13 17:30:12 +00:00
|
|
|
"dictionaryWordCount", "splitWordsCount", "gestureInputCount",
|
2012-08-28 17:26:21 +00:00
|
|
|
"gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount",
|
|
|
|
"revertCommitsCount");
|
2012-08-03 03:22:29 +00:00
|
|
|
private static void logStatistics() {
|
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
final Statistics statistics = researchLogger.mStatistics;
|
2012-08-10 08:54:06 +00:00
|
|
|
researchLogger.enqueueEvent(LOGSTATEMENT_STATISTICS, statistics.mCharCount,
|
|
|
|
statistics.mLetterCount, statistics.mNumberCount, statistics.mSpaceCount,
|
|
|
|
statistics.mDeleteKeyCount, statistics.mWordCount, statistics.mIsEmptyUponStarting,
|
|
|
|
statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(),
|
|
|
|
statistics.mBeforeDeleteKeyCounter.getAverageTime(),
|
|
|
|
statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
|
2012-08-10 21:21:18 +00:00
|
|
|
statistics.mAfterDeleteKeyCounter.getAverageTime(),
|
|
|
|
statistics.mDictionaryWordCount, statistics.mSplitWordsCount,
|
2012-08-28 17:26:21 +00:00
|
|
|
statistics.mGesturesInputCount, statistics.mGesturesCharsCount,
|
|
|
|
statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount,
|
|
|
|
statistics.mRevertCommitsCount);
|
2012-08-03 03:22:29 +00:00
|
|
|
}
|
2012-05-05 04:09:22 +00:00
|
|
|
}
|