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-06-04 19:27:37 +00:00
|
|
|
import android.app.AlertDialog;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.app.Dialog;
|
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-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-03-22 02:13:33 +00:00
|
|
|
import android.inputmethodservice.InputMethodService;
|
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-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;
|
|
|
|
import android.view.MotionEvent;
|
2012-07-19 01:41:15 +00:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.View.OnClickListener;
|
|
|
|
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-07-19 01:41:15 +00:00
|
|
|
import android.widget.Button;
|
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-06-29 14:02:39 +00:00
|
|
|
import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
2012-07-19 01:41:15 +00:00
|
|
|
import com.android.inputmethod.keyboard.LatinKeyboardView;
|
2012-07-20 18:02:39 +00:00
|
|
|
import com.android.inputmethod.latin.Dictionary;
|
|
|
|
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-04-10 01:39:35 +00:00
|
|
|
import java.io.IOException;
|
2012-05-14 16:41:39 +00:00
|
|
|
import java.text.SimpleDateFormat;
|
2012-06-16 00:49:42 +00:00
|
|
|
import java.util.ArrayList;
|
2012-05-14 16:41:39 +00:00
|
|
|
import java.util.Date;
|
2012-06-16 00:49:42 +00:00
|
|
|
import java.util.List;
|
2012-05-14 16:41:39 +00:00
|
|
|
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-06-16 00:49:42 +00:00
|
|
|
private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
|
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-05-31 21:19:38 +00:00
|
|
|
private static final int OUTPUT_FORMAT_VERSION = 1;
|
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-07-18 20:52:41 +00:00
|
|
|
private static final boolean IS_SHOWING_INDICATOR = false;
|
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-07-19 01:00:48 +00:00
|
|
|
private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user
|
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;
|
|
|
|
// The mIntentionalResearchLog records all events for the session, private or not (excepting
|
|
|
|
// passwords). It is written to permanent storage only if the user explicitly commands
|
|
|
|
// the system to do so.
|
|
|
|
/* package */ ResearchLog mIntentionalResearchLog;
|
|
|
|
// LogUnits are queued here and released only when the user requests the intentional log.
|
2012-07-19 01:00:48 +00:00
|
|
|
private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
|
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;
|
|
|
|
private Dictionary mDictionary;
|
2012-06-29 14:02:39 +00:00
|
|
|
private KeyboardSwitcher mKeyboardSwitcher;
|
2012-07-19 01:41:15 +00:00
|
|
|
private InputMethodService mInputMethodService;
|
2012-04-10 01:39:35 +00:00
|
|
|
|
2012-07-19 00:05:48 +00:00
|
|
|
private ResearchLogUploader mResearchLogUploader;
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private ResearchLogger() {
|
|
|
|
}
|
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
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
public void init(final InputMethodService ims, final SharedPreferences prefs,
|
|
|
|
KeyboardSwitcher keyboardSwitcher) {
|
2012-05-14 16:41:39 +00:00
|
|
|
assert ims != null;
|
|
|
|
if (ims == null) {
|
|
|
|
Log.w(TAG, "IMS is null; logging is off");
|
|
|
|
} else {
|
|
|
|
mFilesDir = ims.getFilesDir();
|
|
|
|
if (mFilesDir == null || !mFilesDir.exists()) {
|
|
|
|
Log.w(TAG, "IME storage directory does not exist.");
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
}
|
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-07-19 00:05:48 +00:00
|
|
|
mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir);
|
|
|
|
mResearchLogUploader.start();
|
2012-06-29 14:02:39 +00:00
|
|
|
mKeyboardSwitcher = keyboardSwitcher;
|
2012-07-19 01:41:15 +00:00
|
|
|
mInputMethodService = ims;
|
2012-07-18 20:52:41 +00:00
|
|
|
mPrefs = prefs;
|
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-07-19 01:41:15 +00:00
|
|
|
public void latinKeyboardView_onAttachedToWindow() {
|
|
|
|
maybeShowSplashScreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
|
|
|
|
if (windowToken == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mSplashDialog = new Dialog(mInputMethodService, android.R.style.Theme_Holo_Dialog);
|
|
|
|
mSplashDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
|
|
mSplashDialog.setContentView(R.layout.research_splash);
|
|
|
|
mSplashDialog.setCancelable(true);
|
|
|
|
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.setOnCancelListener(new OnCancelListener() {
|
|
|
|
@Override
|
|
|
|
public void onCancel(DialogInterface dialog) {
|
|
|
|
mInputMethodService.requestHideSelf(0);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
final Button doNotLogButton = (Button) mSplashDialog.findViewById(
|
|
|
|
R.id.research_do_not_log_button);
|
|
|
|
doNotLogButton.setOnClickListener(new OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
onUserLoggingElection(false);
|
|
|
|
mSplashDialog.dismiss();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
final Button doLogButton = (Button) mSplashDialog.findViewById(R.id.research_do_log_button);
|
|
|
|
doLogButton.setOnClickListener(new OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
onUserLoggingElection(true);
|
|
|
|
mSplashDialog.dismiss();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
mSplashDialog.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onUserLoggingElection(final boolean enableLogging) {
|
|
|
|
setLoggingAllowed(enableLogging);
|
|
|
|
if (mPrefs == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final Editor e = mPrefs.edit();
|
|
|
|
e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true);
|
|
|
|
e.apply();
|
|
|
|
}
|
|
|
|
|
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-18 20:52:41 +00:00
|
|
|
private void start() {
|
2012-07-19 01:41:15 +00:00
|
|
|
maybeShowSplashScreen();
|
2012-07-18 20:52:41 +00:00
|
|
|
updateSuspendedState();
|
|
|
|
requestIndicatorRedraw();
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
try {
|
2012-07-18 20:52:41 +00:00
|
|
|
if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
|
|
|
|
mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
mMainResearchLog.start();
|
|
|
|
if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) {
|
|
|
|
mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
2012-04-10 01:39:35 +00:00
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
mIntentionalResearchLog.start();
|
|
|
|
} catch (IOException e) {
|
|
|
|
Log.w(TAG, "Could not start ResearchLogger.");
|
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-06-29 14:02:39 +00:00
|
|
|
if (mMainResearchLog != null) {
|
|
|
|
mMainResearchLog.stop();
|
|
|
|
}
|
2012-07-18 20:52:41 +00:00
|
|
|
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;
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean abort() {
|
|
|
|
boolean didAbortMainLog = false;
|
|
|
|
if (mMainResearchLog != null) {
|
|
|
|
mMainResearchLog.abort();
|
2012-04-18 03:54:33 +00:00
|
|
|
try {
|
2012-06-29 14:02:39 +00:00
|
|
|
mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS);
|
2012-05-14 16:41:39 +00:00
|
|
|
} catch (InterruptedException e) {
|
2012-06-29 14:02:39 +00:00
|
|
|
// interrupted early. carry on.
|
|
|
|
}
|
|
|
|
if (mMainResearchLog.isAbortSuccessful()) {
|
|
|
|
didAbortMainLog = true;
|
2012-04-18 03:54:33 +00:00
|
|
|
}
|
2012-07-18 20:52:41 +00:00
|
|
|
mMainResearchLog = null;
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
boolean didAbortIntentionalLog = false;
|
|
|
|
if (mIntentionalResearchLog != null) {
|
|
|
|
mIntentionalResearchLog.abort();
|
2012-06-04 19:27:37 +00:00
|
|
|
try {
|
2012-06-29 14:02:39 +00:00
|
|
|
mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
// interrupted early. carry on.
|
|
|
|
}
|
|
|
|
if (mIntentionalResearchLog.isAbortSuccessful()) {
|
|
|
|
didAbortIntentionalLog = true;
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
2012-07-18 20:52:41 +00:00
|
|
|
mIntentionalResearchLog = null;
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
return didAbortMainLog && didAbortIntentionalLog;
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
/* package */ void flush() {
|
|
|
|
if (mMainResearchLog != null) {
|
|
|
|
mMainResearchLog.flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-06-04 19:27:37 +00:00
|
|
|
}
|
|
|
|
|
2012-07-20 18:02:39 +00:00
|
|
|
public void presentResearchDialog(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-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-18 20:52:41 +00:00
|
|
|
if (showEnable) {
|
|
|
|
if (!sIsLogging) {
|
|
|
|
setLoggingAllowed(true);
|
|
|
|
}
|
|
|
|
resumeLogging();
|
2012-07-19 01:00:48 +00:00
|
|
|
Toast.makeText(latinIME,
|
|
|
|
R.string.research_notify_session_logging_enabled,
|
2012-07-18 20:52:41 +00:00
|
|
|
Toast.LENGTH_LONG).show();
|
2012-06-04 19:27:37 +00:00
|
|
|
} else {
|
2012-06-29 14:02:39 +00:00
|
|
|
Toast toast = Toast.makeText(latinIME,
|
2012-07-19 01:00:48 +00:00
|
|
|
R.string.research_notify_session_log_deleting,
|
|
|
|
Toast.LENGTH_LONG);
|
2012-06-29 14:02:39 +00:00
|
|
|
toast.show();
|
|
|
|
boolean isLogDeleted = abort();
|
2012-07-18 20:52:41 +00:00
|
|
|
final long currentTime = System.currentTimeMillis();
|
|
|
|
final long resumeTime = currentTime + 1000 * 60 *
|
|
|
|
SUSPEND_DURATION_IN_MINUTES;
|
|
|
|
suspendLoggingUntil(resumeTime);
|
2012-06-29 14:02:39 +00:00
|
|
|
toast.cancel();
|
2012-07-19 01:00:48 +00:00
|
|
|
Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
|
2012-07-18 20:52:41 +00:00
|
|
|
Toast.LENGTH_LONG).show();
|
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-19 01:00:48 +00:00
|
|
|
private boolean mInFeedbackDialog = false;
|
|
|
|
public void presentFeedbackDialog(LatinIME latinIME) {
|
|
|
|
mInFeedbackDialog = true;
|
|
|
|
latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
|
|
|
|
}
|
|
|
|
|
|
|
|
private ResearchLog mFeedbackLog;
|
|
|
|
private List<LogUnit> mFeedbackQueue;
|
|
|
|
private ResearchLog mSavedMainResearchLog;
|
|
|
|
private ResearchLog mSavedIntentionalResearchLog;
|
|
|
|
private List<LogUnit> mSavedIntentionalResearchLogQueue;
|
|
|
|
|
|
|
|
private void saveLogsForFeedback() {
|
|
|
|
mFeedbackLog = mIntentionalResearchLog;
|
|
|
|
if (mIntentionalResearchLogQueue != null) {
|
|
|
|
mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue);
|
|
|
|
} else {
|
|
|
|
mFeedbackQueue = null;
|
|
|
|
}
|
|
|
|
mSavedMainResearchLog = mMainResearchLog;
|
|
|
|
mSavedIntentionalResearchLog = mIntentionalResearchLog;
|
|
|
|
mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue;
|
|
|
|
|
|
|
|
mMainResearchLog = null;
|
|
|
|
mIntentionalResearchLog = null;
|
|
|
|
mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5;
|
|
|
|
public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
|
|
|
|
if (includeHistory && mFeedbackLog != null) {
|
|
|
|
try {
|
|
|
|
LogUnit headerLogUnit = new LogUnit();
|
|
|
|
headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
|
|
|
|
mFeedbackLog.publishAllEvents(headerLogUnit);
|
|
|
|
for (LogUnit logUnit : mFeedbackQueue) {
|
|
|
|
mFeedbackLog.publishAllEvents(logUnit);
|
|
|
|
}
|
|
|
|
userFeedback(mFeedbackLog, feedbackContents);
|
|
|
|
mFeedbackLog.stop();
|
|
|
|
try {
|
|
|
|
mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
|
|
|
|
mIntentionalResearchLog.start();
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} finally {
|
|
|
|
mIntentionalResearchLogQueue.clear();
|
|
|
|
}
|
|
|
|
mResearchLogUploader.uploadNow(null);
|
|
|
|
} else {
|
|
|
|
// create a separate ResearchLog just for feedback
|
|
|
|
final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir));
|
|
|
|
try {
|
|
|
|
feedbackLog.start();
|
|
|
|
userFeedback(feedbackLog, feedbackContents);
|
|
|
|
feedbackLog.stop();
|
|
|
|
feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
|
|
|
|
mResearchLogUploader.uploadNow(null);
|
|
|
|
} catch (IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onLeavingSendFeedbackDialog() {
|
|
|
|
mInFeedbackDialog = false;
|
|
|
|
mMainResearchLog = mSavedMainResearchLog;
|
|
|
|
mIntentionalResearchLog = mSavedIntentionalResearchLog;
|
|
|
|
mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue;
|
|
|
|
}
|
|
|
|
|
2012-06-16 00:49:42 +00:00
|
|
|
public void initSuggest(Suggest suggest) {
|
|
|
|
mSuggest = suggest;
|
|
|
|
}
|
|
|
|
|
2012-06-13 23:37:20 +00:00
|
|
|
private void setIsPasswordView(boolean isPasswordView) {
|
|
|
|
mIsPasswordView = isPasswordView;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isAllowedToLog() {
|
2012-07-18 20:52:41 +00:00
|
|
|
return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging;
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void requestIndicatorRedraw() {
|
|
|
|
// invalidate any existing graphics
|
2012-07-18 20:52:41 +00:00
|
|
|
if (IS_SHOWING_INDICATOR) {
|
|
|
|
if (mKeyboardSwitcher != null) {
|
|
|
|
mKeyboardSwitcher.getKeyboardView().invalidateAllKeys();
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
}
|
2012-06-13 23:37:20 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String CURRENT_TIME_KEY = "_ct";
|
|
|
|
private static final String UPTIME_KEY = "_ut";
|
|
|
|
private static final String EVENT_TYPE_KEY = "_ty";
|
|
|
|
private static final Object[] EVENTKEYS_NULLVALUES = {};
|
2012-03-22 02:13:33 +00:00
|
|
|
|
2012-06-16 00:49:42 +00:00
|
|
|
private LogUnit mCurrentLogUnit = new LogUnit();
|
|
|
|
|
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-06-16 00:49:42 +00:00
|
|
|
* This 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.
|
2012-05-14 16:41:39 +00:00
|
|
|
*
|
|
|
|
* @param keys an array containing a descriptive name for the event, followed by the keys
|
|
|
|
* @param values an array of values, either a String or Number. length should be one
|
|
|
|
* less than the keys array
|
|
|
|
*/
|
2012-06-16 00:49:42 +00:00
|
|
|
private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys,
|
|
|
|
final Object[] values) {
|
2012-05-14 16:41:39 +00:00
|
|
|
assert values.length + 1 == keys.length;
|
2012-06-29 14:02:39 +00:00
|
|
|
if (isAllowedToLog()) {
|
|
|
|
mCurrentLogUnit.addLogAtom(keys, values, true);
|
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Buffer a research log event, flaggint it as not privacy-sensitive.
|
|
|
|
*
|
|
|
|
* This event contains no potentially private information. Even if the word that this event
|
|
|
|
* is privacy-sensitive, this event can still safely be sent to the output log. The system
|
|
|
|
* waits until the containing word is known so that this event can be written in the proper
|
|
|
|
* temporal order with other events that may be privacy sensitive.
|
|
|
|
*
|
|
|
|
* @param keys an array containing a descriptive name for the event, followed by the keys
|
|
|
|
* @param values an array of values, either a String or Number. length should be one
|
|
|
|
* less than the keys array
|
|
|
|
*/
|
|
|
|
private synchronized void enqueueEvent(final String[] keys, final Object[] values) {
|
|
|
|
assert values.length + 1 == keys.length;
|
2012-06-29 14:02:39 +00:00
|
|
|
if (isAllowedToLog()) {
|
|
|
|
mCurrentLogUnit.addLogAtom(keys, values, false);
|
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-06-21 03:23:19 +00:00
|
|
|
// Used to track how often words are logged. Too-frequent logging can leak
|
|
|
|
// semantics, disclosing private data.
|
|
|
|
/* package for test */ static class LoggingFrequencyState {
|
|
|
|
private static final int DEFAULT_WORD_LOG_FREQUENCY = 10;
|
|
|
|
private int mWordsRemainingToSkip;
|
|
|
|
private final int mFrequency;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tracks how often words may be uploaded.
|
|
|
|
*
|
|
|
|
* @param frequency 1=Every word, 2=Every other word, etc.
|
|
|
|
*/
|
|
|
|
public LoggingFrequencyState(int frequency) {
|
|
|
|
mFrequency = frequency;
|
|
|
|
mWordsRemainingToSkip = mFrequency;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onWordLogged() {
|
|
|
|
mWordsRemainingToSkip = mFrequency;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onWordNotLogged() {
|
|
|
|
if (mWordsRemainingToSkip > 1) {
|
|
|
|
mWordsRemainingToSkip--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isSafeToLog() {
|
|
|
|
return mWordsRemainingToSkip <= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* package for test */ LoggingFrequencyState mLoggingFrequencyState =
|
|
|
|
new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY);
|
|
|
|
|
2012-06-19 00:48:00 +00:00
|
|
|
/* package for test */ boolean isPrivacyThreat(String word) {
|
2012-06-21 03:23:19 +00:00
|
|
|
// Current checks:
|
|
|
|
// - Word not in dictionary
|
|
|
|
// - Word contains numbers
|
|
|
|
// - Privacy-safe word not logged recently
|
2012-06-19 00:48:00 +00:00
|
|
|
if (TextUtils.isEmpty(word)) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-21 03:23:19 +00:00
|
|
|
if (!mLoggingFrequencyState.isSafeToLog()) {
|
|
|
|
return true;
|
|
|
|
}
|
2012-06-19 00:48:00 +00:00
|
|
|
final int length = word.length();
|
|
|
|
boolean hasLetter = false;
|
|
|
|
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
|
|
|
|
final int codePoint = Character.codePointAt(word, i);
|
|
|
|
if (Character.isDigit(codePoint)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (Character.isLetter(codePoint)) {
|
|
|
|
hasLetter = true;
|
|
|
|
break; // Word may contain digits, but will only be allowed if in the dictionary.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hasLetter) {
|
|
|
|
if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) {
|
|
|
|
mDictionary = mSuggest.getMainDictionary();
|
|
|
|
}
|
|
|
|
if (mDictionary == null) {
|
|
|
|
// Can't access dictionary. Assume privacy threat.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return !(mDictionary.isValidWord(word));
|
|
|
|
}
|
|
|
|
// No letters, no numbers. Punctuation, space, or something else.
|
|
|
|
return false;
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-06-21 03:23:19 +00:00
|
|
|
private void onWordComplete(String word) {
|
2012-06-29 14:02:39 +00:00
|
|
|
if (isPrivacyThreat(word)) {
|
|
|
|
publishLogUnit(mCurrentLogUnit, true);
|
2012-06-21 03:23:19 +00:00
|
|
|
mLoggingFrequencyState.onWordNotLogged();
|
|
|
|
} else {
|
2012-06-29 14:02:39 +00:00
|
|
|
publishLogUnit(mCurrentLogUnit, false);
|
2012-06-21 03:23:19 +00:00
|
|
|
mLoggingFrequencyState.onWordLogged();
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
mCurrentLogUnit = new LogUnit();
|
2012-06-21 03:23:19 +00:00
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) {
|
2012-07-18 20:52:41 +00:00
|
|
|
if (!isAllowedToLog()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (mMainResearchLog == null) {
|
|
|
|
return;
|
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
if (isPrivacySensitive) {
|
|
|
|
mMainResearchLog.publishPublicEvents(logUnit);
|
|
|
|
} else {
|
|
|
|
mMainResearchLog.publishAllEvents(logUnit);
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
2012-06-29 14:02:39 +00:00
|
|
|
mIntentionalResearchLogQueue.add(logUnit);
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
/* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) {
|
|
|
|
publishLogUnit(mCurrentLogUnit, isPrivacySensitive);
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
static class LogUnit {
|
2012-06-16 00:49:42 +00:00
|
|
|
private final List<String[]> mKeysList = new ArrayList<String[]>();
|
|
|
|
private final List<Object[]> mValuesList = new ArrayList<Object[]>();
|
|
|
|
private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>();
|
|
|
|
|
|
|
|
private void addLogAtom(final String[] keys, final Object[] values,
|
|
|
|
final Boolean isPotentiallyPrivate) {
|
|
|
|
mKeysList.add(keys);
|
|
|
|
mValuesList.add(values);
|
|
|
|
mIsPotentiallyPrivate.add(isPotentiallyPrivate);
|
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
public void publishPublicEventsTo(ResearchLog researchLog) {
|
|
|
|
final int size = mKeysList.size();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
if (!mIsPotentiallyPrivate.get(i)) {
|
|
|
|
researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
|
|
|
|
}
|
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
public void publishAllEventsTo(ResearchLog researchLog) {
|
|
|
|
final int size = mKeysList.size();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
|
2012-06-16 00:49:42 +00:00
|
|
|
}
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
if (mDictionary == null) {
|
|
|
|
return WORD_REPLACEMENT_STRING;
|
|
|
|
}
|
|
|
|
if (mDictionary.isValidWord(word)) {
|
|
|
|
return word;
|
|
|
|
}
|
|
|
|
return WORD_REPLACEMENT_STRING;
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
// Special methods related to startup, shutdown, logging itself
|
|
|
|
|
2012-06-29 14:02:39 +00:00
|
|
|
private static final String[] EVENTKEYS_INTENTIONAL_LOG = {
|
|
|
|
"IntentionalLog"
|
|
|
|
};
|
2012-07-18 20:52:41 +00:00
|
|
|
|
|
|
|
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();
|
2012-07-19 01:00:48 +00:00
|
|
|
if (researchLogger.mInFeedbackDialog) {
|
|
|
|
researchLogger.saveLogsForFeedback();
|
|
|
|
}
|
2012-07-18 20:52:41 +00:00
|
|
|
researchLogger.start();
|
|
|
|
if (editorInfo != null) {
|
2012-07-19 01:41:15 +00:00
|
|
|
final Context context = researchLogger.mInputMethodService;
|
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;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-07-19 01:00:48 +00:00
|
|
|
private static final String[] EVENTKEYS_USER_FEEDBACK = {
|
|
|
|
"UserFeedback", "FeedbackContents"
|
|
|
|
};
|
|
|
|
|
|
|
|
private void userFeedback(ResearchLog researchLog, String feedbackContents) {
|
|
|
|
// this method is special; it directs the feedbackContents to a particular researchLog
|
|
|
|
final LogUnit logUnit = new LogUnit();
|
|
|
|
final Object[] values = {
|
|
|
|
feedbackContents
|
|
|
|
};
|
|
|
|
logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false);
|
|
|
|
researchLog.publishAllEvents(logUnit);
|
|
|
|
}
|
|
|
|
|
2012-07-18 20:52:41 +00:00
|
|
|
// Regular logging methods
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
|
2012-05-14 16:41:39 +00:00
|
|
|
"pressure"
|
|
|
|
};
|
|
|
|
public static void latinKeyboardView_processMotionEvent(final MotionEvent me, final int action,
|
|
|
|
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);
|
|
|
|
final Object[] values = {
|
|
|
|
actionString, eventTime, id, x, y, size, pressure
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEOnCodeInput", "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) {
|
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
|
2012-03-22 02:13:33 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_CORRECTION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LogCorrection", "subgroup", "before", "after", "position"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
|
|
|
public static void logCorrection(final String subgroup, final String before, final String after,
|
|
|
|
final int position) {
|
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values);
|
2012-04-10 01:39:35 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
|
2012-04-13 00:13:52 +00:00
|
|
|
final String autoCorrection) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection)
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
|
2012-04-13 00:13:52 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEDeleteSurroundingText", "length"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_deleteSurroundingText(final int length) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
length
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEDoubleSpaceAutoPeriod"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_doubleSpaceAutoPeriod() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void latinIME_onDisplayCompletions(
|
|
|
|
final CompletionInfo[] applicationSpecifiedCompletions) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
applicationSpecifiedCompletions
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS,
|
|
|
|
values);
|
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-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEOnWindowHidden", "isTextTruncated", "text"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
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) {
|
|
|
|
ic.beginBatchEdit();
|
|
|
|
ic.performContextMenuAction(android.R.id.selectAll);
|
|
|
|
CharSequence charSequence = ic.getSelectedText(0);
|
|
|
|
ic.setSelection(savedSelectionStart, savedSelectionEnd);
|
|
|
|
ic.endBatchEdit();
|
|
|
|
sLatinIMEExpectingUpdateSelection = true;
|
2012-06-16 00:49:42 +00:00
|
|
|
final Object[] values = new Object[2];
|
|
|
|
if (OUTPUT_ENTIRE_BUFFER) {
|
|
|
|
if (TextUtils.isEmpty(charSequence)) {
|
2012-05-14 16:41:39 +00:00
|
|
|
values[0] = false;
|
2012-06-16 00:49:42 +00:00
|
|
|
values[1] = "";
|
|
|
|
} 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);
|
|
|
|
values[0] = true;
|
|
|
|
values[1] = truncatedCharSequence.toString();
|
|
|
|
} else {
|
|
|
|
values[0] = false;
|
|
|
|
values[1] = charSequence.toString();
|
|
|
|
}
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
} else {
|
|
|
|
values[0] = true;
|
|
|
|
values[1] = "";
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
|
2012-06-29 14:02:39 +00:00
|
|
|
// Play it safe. Remove privacy-sensitive events.
|
|
|
|
researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true);
|
|
|
|
researchLogger.mCurrentLogUnit = new LogUnit();
|
2012-07-19 01:41:15 +00:00
|
|
|
getInstance().stop();
|
2012-05-02 05:09:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
|
2012-05-14 16:41:39 +00:00
|
|
|
"oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
|
2012-05-29 16:33:09 +00:00
|
|
|
"expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
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-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart,
|
|
|
|
newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection,
|
2012-06-16 00:49:42 +00:00
|
|
|
expectingUpdateSelectionFromLogger, scrubbedWord
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values);
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final String[] EVENTKEYS_LATINIME_PERFORMEDITORACTION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEPerformEditorAction", "imeActionNext"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_performEditorAction(final int imeActionNext) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
imeActionNext
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-12 04:42:22 +00:00
|
|
|
public static void latinIME_pickApplicationSpecifiedCompletion(final int index,
|
2012-06-12 17:56:03 +00:00
|
|
|
final CharSequence cs, int x, int y) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-06-12 17:56:03 +00:00
|
|
|
index, cs, x, y
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, values);
|
2012-04-12 04:42:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-12 04:42:22 +00:00
|
|
|
public static void latinIME_pickSuggestionManually(final String replacedWord,
|
|
|
|
final int index, CharSequence suggestion, int x, int y) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
scrubDigitsFromString(replacedWord), index, suggestion == null ? null :
|
|
|
|
scrubDigitsFromString(suggestion.toString()), x, y
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
final ResearchLogger researchLogger = getInstance();
|
|
|
|
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY,
|
|
|
|
values);
|
2012-04-12 04:42:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-12 04:42:22 +00:00
|
|
|
public static void latinIME_punctuationSuggestion(final int index,
|
|
|
|
final CharSequence suggestion, int x, int y) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-06-12 17:56:03 +00:00
|
|
|
index, suggestion, x, y
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMERevertDoubleSpaceWhileInBatchEdit"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_revertDoubleSpaceWhileInBatchEdit() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT,
|
2012-05-14 16:41:39 +00:00
|
|
|
EVENTKEYS_NULLVALUES);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMERevertSwapPunctuation"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_revertSwapPunctuation() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMESendKeyCodePoint", "code"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_sendKeyCodePoint(final int code) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
Keyboard.printableCode(scrubDigitFromCodePoint(code))
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMESwapSwapperAndSpaceWhileInBatchEdit"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT,
|
2012-05-14 16:41:39 +00:00
|
|
|
EVENTKEYS_NULLVALUES);
|
2012-04-12 04:42:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinKeyboardViewOnLongPress"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void latinKeyboardView_onLongPress() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width",
|
2012-05-29 16:33:09 +00:00
|
|
|
"modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey",
|
|
|
|
"passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled",
|
|
|
|
"isMultiLine", "tw", "th", "keys"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 03:54:33 +00:00
|
|
|
public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
|
2012-05-14 16:41:39 +00:00
|
|
|
if (keyboard != null) {
|
2012-05-29 16:33:09 +00:00
|
|
|
final KeyboardId kid = keyboard.mId;
|
2012-06-15 05:15:27 +00:00
|
|
|
final boolean isPasswordView = kid.passwordInput();
|
2012-07-18 20:52:41 +00:00
|
|
|
getInstance().setIsPasswordView(isPasswordView);
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
2012-05-29 16:33:09 +00:00
|
|
|
KeyboardId.elementIdToName(kid.mElementId),
|
|
|
|
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
|
|
|
|
kid.mOrientation,
|
|
|
|
kid.mWidth,
|
|
|
|
KeyboardId.modeName(kid.mMode),
|
|
|
|
kid.imeAction(),
|
|
|
|
kid.navigateNext(),
|
|
|
|
kid.navigatePrevious(),
|
|
|
|
kid.mClobberSettingsKey,
|
2012-06-13 23:37:20 +00:00
|
|
|
isPasswordView,
|
2012-05-29 16:33:09 +00:00
|
|
|
kid.mShortcutKeyEnabled,
|
|
|
|
kid.mHasShortcutKey,
|
|
|
|
kid.mLanguageSwitchKeyEnabled,
|
|
|
|
kid.isMultiLine(),
|
|
|
|
keyboard.mOccupiedWidth,
|
|
|
|
keyboard.mOccupiedHeight,
|
|
|
|
keyboard.mKeys
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_LATINKEYBOARDVIEW_SETKEYBOARD, values);
|
2012-06-13 23:37:20 +00:00
|
|
|
getInstance().setIsPasswordView(isPasswordView);
|
2012-04-18 03:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"LatinIMERevertCommit", "originallyTypedWord"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-04-18 01:34:02 +00:00
|
|
|
public static void latinIME_revertCommit(final String originallyTypedWord) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
originallyTypedWord
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values);
|
2012-04-18 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"PointerTrackerCallListenerOnCancelInput"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_callListenerOnCancelInput() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT,
|
2012-05-14 16:41:39 +00:00
|
|
|
EVENTKEYS_NULLVALUES);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y",
|
2012-05-14 16:41:39 +00:00
|
|
|
"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) {
|
|
|
|
CharSequence outputText = key.mOutputText;
|
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null
|
|
|
|
: scrubDigitsFromString(outputText.toString()),
|
|
|
|
x, y, ignoreModifierKey, altersCode, key.isEnabled()
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey",
|
2012-05-14 16:41:39 +00:00
|
|
|
"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) {
|
|
|
|
final Object[] values = {
|
2012-06-15 05:15:27 +00:00
|
|
|
Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
|
|
|
|
ignoreModifierKey, key.isEnabled()
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"PointerTrackerOnDownEvent", "deltaT", "distanceSquared"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-03-30 20:15:46 +00:00
|
|
|
public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
|
2012-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
deltaT, distanceSquared
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
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-05-14 16:41:39 +00:00
|
|
|
final Object[] values = {
|
|
|
|
x, y, lastX, lastY
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values);
|
2012-03-30 20:15:46 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 16:41:39 +00:00
|
|
|
private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
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) {
|
|
|
|
final Object[] values = {
|
|
|
|
me.toString()
|
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(
|
|
|
|
EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values);
|
2012-05-14 16:41:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final String[] EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS = {
|
2012-06-04 18:55:33 +00:00
|
|
|
"SuggestionsViewSetSuggestions", "suggestedWords"
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
|
|
|
public static void suggestionsView_setSuggestions(final SuggestedWords suggestedWords) {
|
|
|
|
if (suggestedWords != null) {
|
|
|
|
final Object[] values = {
|
2012-05-29 16:33:09 +00:00
|
|
|
suggestedWords
|
2012-05-14 16:41:39 +00:00
|
|
|
};
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_SUGGESTIONSVIEW_SETSUGGESTIONS,
|
|
|
|
values);
|
2012-04-09 17:53:17 +00:00
|
|
|
}
|
|
|
|
}
|
2012-06-04 19:27:37 +00:00
|
|
|
|
|
|
|
private static final String[] EVENTKEYS_USER_TIMESTAMP = {
|
|
|
|
"UserTimestamp"
|
|
|
|
};
|
|
|
|
public void userTimestamp() {
|
2012-06-16 00:49:42 +00:00
|
|
|
getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES);
|
2012-06-04 19:27:37 +00:00
|
|
|
}
|
2012-05-05 04:09:22 +00:00
|
|
|
}
|