diff options
Diffstat (limited to 'java/src')
10 files changed, 202 insertions, 125 deletions
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java index 7df266ef2..c2aade64d 100644 --- a/java/src/com/android/inputmethod/latin/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/DebugSettings.java @@ -57,7 +57,7 @@ public final class DebugSettings extends PreferenceFragment if (usabilityStudyPref instanceof CheckBoxPreference) { final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, - ResearchLogger.DEFAULT_USABILITY_STUDY_MODE)); + LatinImeLogger.getUsabilityStudyMode(prefs))); checkbox.setSummary(R.string.settings_warning_researcher_mode); } final Preference statisticsLoggingPref = findPreference(PREF_STATISTICS_LOGGING); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index e9347461c..af494c4f4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -428,7 +428,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction initSuggest(); if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.getInstance().init(this, mKeyboardSwitcher); + ResearchLogger.getInstance().init(this, mKeyboardSwitcher, mSuggest); } mDisplayOrientation = getResources().getConfiguration().orientation; @@ -563,6 +563,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mSettings.onDestroy(); unregisterReceiver(mReceiver); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.getInstance().onDestroy(); + } // TODO: The experimental version is not supported by the Dictionary Pack Service yet. if (!ProductionFlag.IS_EXPERIMENTAL) { unregisterReceiver(mDictionaryPackInstallReceiver); diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index e4e8b94b2..3f2b0a3f4 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -37,6 +37,10 @@ public final class LatinImeLogger implements SharedPreferences.OnSharedPreferenc public static void commit() { } + public static boolean getUsabilityStudyMode(final SharedPreferences prefs) { + return false; + } + public static void onDestroy() { } diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index acfcd5354..7a604dc6a 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -28,6 +28,7 @@ import android.os.Process; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.io.BufferedReader; @@ -77,6 +78,7 @@ public final class Utils { private RingCharBuffer() { // Intentional empty constructor for singleton. } + @UsedForTesting public static RingCharBuffer getInstance() { return sRingCharBuffer; } @@ -93,6 +95,7 @@ public final class Utils { return ret < 0 ? ret + BUFSIZE : ret; } // TODO: accept code points + @UsedForTesting public void push(char c, int x, int y) { if (!mEnabled) return; mCharBuf[mEnd] = c; diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index e91976a03..839e2b7ba 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -16,7 +16,6 @@ package com.android.inputmethod.research; -import android.content.SharedPreferences; import android.os.SystemClock; import android.text.TextUtils; import android.util.JsonWriter; @@ -45,7 +44,7 @@ import java.util.List; * will not violate the user's privacy. Checks for this may include whether other LogUnits have * been published recently, or whether the LogUnit contains numbers, etc. */ -/* package */ class LogUnit { +public class LogUnit { private static final String TAG = LogUnit.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java index 45b83dd76..9aa60f859 100644 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -18,6 +18,7 @@ package com.android.inputmethod.research; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.define.ProductionFlag; @@ -64,7 +65,11 @@ public abstract class MainLogBuffer extends FixedLogBuffer { // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. public static final int N_GRAM_SIZE = 2; - private Suggest mSuggest; + // TODO: Remove dependence on Suggest, and pass in Dictionary as a parameter to an appropriate + // method. + private final Suggest mSuggest; + @UsedForTesting + private Dictionary mDictionaryForTesting; private boolean mIsStopping = false; /* package for test */ int mNumWordsBetweenNGrams; @@ -73,17 +78,23 @@ public abstract class MainLogBuffer extends FixedLogBuffer { // after a sample is taken. /* package for test */ int mNumWordsUntilSafeToSample; - public MainLogBuffer(final int wordsBetweenSamples, final int numInitialWordsToIgnore) { + public MainLogBuffer(final int wordsBetweenSamples, final int numInitialWordsToIgnore, + final Suggest suggest) { super(N_GRAM_SIZE + wordsBetweenSamples); mNumWordsBetweenNGrams = wordsBetweenSamples; mNumWordsUntilSafeToSample = DEBUG ? 0 : numInitialWordsToIgnore; + mSuggest = suggest; } - public void setSuggest(final Suggest suggest) { - mSuggest = suggest; + @UsedForTesting + /* package for test */ void setDictionaryForTesting(final Dictionary dictionary) { + mDictionaryForTesting = dictionary; } private Dictionary getDictionary() { + if (mDictionaryForTesting != null) { + return mDictionaryForTesting; + } if (mSuggest == null || !mSuggest.hasMainDictionary()) return null; return mSuggest.getMainDictionary(); } diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 99d84938f..9016e23b3 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -38,12 +38,19 @@ import java.util.concurrent.TimeUnit; /** * 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. + * This class logs operations on the IME keyboard, including what the user has typed. Data is + * written to a {@link JsonWriter}, which will write to a local file. + * + * The JsonWriter is created on-demand by calling {@link #getInitializedJsonWriterLocked}. + * + * This class uses an executor to perform file-writing operations on a separate thread. It also + * tries to avoid creating unnecessary files if there is nothing to write. It also handles + * flushing, making sure it happens, but not too frequently. * * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}. */ public class ResearchLog { + // TODO: Automatically initialize the JsonWriter rather than requiring the caller to manage it. private static final String TAG = ResearchLog.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; private static final long FLUSH_DELAY_IN_MS = 1000 * 5; @@ -87,6 +94,12 @@ public class ResearchLog { mContext = context; } + /** + * Waits for any publication requests to finish and closes the {@link JsonWriter} used for + * output. + * + * See class comment for details about {@code JsonWriter} construction. + */ public synchronized void close(final Runnable onClosed) { mExecutor.submit(new Callable<Object>() { @Override @@ -94,20 +107,15 @@ public class ResearchLog { try { if (mHasWrittenData) { mJsonWriter.endArray(); - mJsonWriter.flush(); - mJsonWriter.close(); - if (DEBUG) { - Log.d(TAG, "wrote log to " + mFile); - } mHasWrittenData = false; - } else { - if (DEBUG) { - Log.d(TAG, "close() called, but no data, not outputting"); - } + } + mJsonWriter.flush(); + mJsonWriter.close(); + if (DEBUG) { + Log.d(TAG, "wrote log to " + mFile); } } catch (Exception e) { - Log.d(TAG, "error when closing ResearchLog:"); - e.printStackTrace(); + Log.d(TAG, "error when closing ResearchLog:", e); } finally { if (mFile != null && mFile.exists()) { mFile.setWritable(false, false); @@ -125,6 +133,12 @@ public class ResearchLog { private boolean mIsAbortSuccessful; + /** + * Waits for publication requests to finish, closes the {@link JsonWriter}, but then deletes the + * backing file used for output. + * + * See class comment for details about {@code JsonWriter} construction. + */ public synchronized void abort() { mExecutor.submit(new Callable<Object>() { @Override @@ -184,6 +198,12 @@ public class ResearchLog { mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); } + /** + * Queues up {@code logUnit} to be published in the background. + * + * @param logUnit the {@link LogUnit} to be published + * @param canIncludePrivateData whether private data in the LogUnit should be included + */ public synchronized void publish(final LogUnit logUnit, final boolean canIncludePrivateData) { try { mExecutor.submit(new Callable<Object>() { diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 25633d630..e705ddda1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -122,11 +122,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // field holds a channel name, the developer does not have to re-enter it when using the // feedback mechanism to generate multiple tests. private static final boolean FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD = false; - public static final boolean DEFAULT_USABILITY_STUDY_MODE = false; /* package */ static boolean sIsLogging = false; private static final int OUTPUT_FORMAT_VERSION = 5; private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash"; /* package */ static final String LOG_FILENAME_PREFIX = "researchLog"; private static final String LOG_FILENAME_SUFFIX = ".txt"; /* package */ static final String USER_RECORDING_FILENAME_PREFIX = "recording"; @@ -154,7 +152,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // constants related to specific log points private static final String WHITESPACE_SEPARATORS = " \t\n\r"; private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 - private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid"; private static final String PREF_RESEARCH_SAVED_CHANNEL = "pref_research_saved_channel"; private static final ResearchLogger sInstance = new ResearchLogger(); @@ -162,7 +159,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static String sAllowedAccountDomain = null; // to write to a different filename, e.g., for testing, set mFile before calling start() /* package */ File mFilesDir; - /* package */ String mUUIDString; /* package */ ResearchLog mMainResearchLog; // mFeedbackLog records all events for the session, private or not (excepting // passwords). It is written to permanent storage only if the user explicitly commands @@ -208,7 +204,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private Intent mUploadIntent; private Intent mUploadNowIntent; - private LogUnit mCurrentLogUnit = new LogUnit(); + /* package for test */ LogUnit mCurrentLogUnit = new LogUnit(); // 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, @@ -237,50 +233,44 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return sInstance; } - public void init(final LatinIME latinIME, final KeyboardSwitcher keyboardSwitcher) { + public void init(final LatinIME latinIME, final KeyboardSwitcher keyboardSwitcher, + final Suggest suggest) { assert latinIME != null; - if (latinIME == null) { - Log.w(TAG, "IMS is null; logging is off"); - } else { - mFilesDir = latinIME.getFilesDir(); - if (mFilesDir == null || !mFilesDir.exists()) { - Log.w(TAG, "IME storage directory does not exist."); - } - } - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME); - if (prefs != null) { - mUUIDString = getUUID(prefs); - if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) { - Editor e = prefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE); - e.apply(); - } - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); - prefs.registerOnSharedPreferenceChangeListener(this); - - 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(); - } + mLatinIME = latinIME; + mFilesDir = latinIME.getFilesDir(); + if (mFilesDir == null || !mFilesDir.exists()) { + Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); + return; } + mPrefs = PreferenceManager.getDefaultSharedPreferences(latinIME); + mPrefs.registerOnSharedPreferenceChangeListener(this); + + // Initialize fields from preferences + sIsLogging = ResearchSettings.readResearchLoggerEnabledFlag(mPrefs); + + // Initialize fields from resources final Resources res = latinIME.getResources(); sAccountType = res.getString(R.string.research_account_type); sAllowedAccountDomain = res.getString(R.string.research_allowed_account_domain); - mLatinIME = latinIME; - mPrefs = prefs; + + // Cleanup logging directory + // TODO: Move this and other file-related components to separate file. + final long lastCleanupTime = mPrefs.getLong(PREF_LAST_CLEANUP_TIME, 0L); + final long now = System.currentTimeMillis(); + if (now - lastCleanupTime > DURATION_BETWEEN_DIR_CLEANUP_IN_MS) { + final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS; + cleanupLoggingDir(mFilesDir, timeHorizon); + mPrefs.edit().putLong(PREF_LAST_CLEANUP_TIME, now).apply(); + } + + // Initialize external services mUploadIntent = new Intent(mLatinIME, UploaderService.class); mUploadNowIntent = new Intent(mLatinIME, UploaderService.class); mUploadNowIntent.putExtra(UploaderService.EXTRA_UPLOAD_UNCONDITIONALLY, true); - mReplayer.setKeyboardSwitcher(keyboardSwitcher); - if (ProductionFlag.IS_EXPERIMENTAL) { scheduleUploadingService(mLatinIME); } + mReplayer.setKeyboardSwitcher(keyboardSwitcher); } /** @@ -322,14 +312,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMainKeyboardView = null; } - private boolean hasSeenSplash() { - return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false); + public void onDestroy() { + if (mPrefs != null) { + mPrefs.unregisterOnSharedPreferenceChangeListener(this); + } } private Dialog mSplashDialog = null; private void maybeShowSplashScreen() { - if (hasSeenSplash()) { + if (ResearchSettings.readHasSeenSplash(mPrefs)) { return; } if (mSplashDialog != null && mSplashDialog.isShowing()) { @@ -382,32 +374,23 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } public void onUserLoggingConsent() { - setLoggingAllowed(true); if (mPrefs == null) { - return; + mPrefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); + if (mPrefs == null) return; } - final Editor e = mPrefs.edit(); - e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true); - e.apply(); + sIsLogging = true; + ResearchSettings.writeResearchLoggerEnabledFlag(mPrefs, true); + ResearchSettings.writeHasSeenSplash(mPrefs, true); restart(); } - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; - } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); - sIsLogging = enableLogging; - } - private static int sLogFileCounter = 0; private File createLogFile(final File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(LOG_FILENAME_PREFIX).append('-'); - sb.append(mUUIDString).append('-'); + final String uuid = ResearchSettings.readResearchLoggerUuid(mPrefs); + sb.append(uuid).append('-'); sb.append(TIMESTAMP_DATEFORMAT.format(new Date())).append('-'); // Sometimes logFiles are created within milliseconds of each other. Append a counter to // separate these. @@ -425,7 +408,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private File createUserRecordingFile(final File filesDir) { final StringBuilder sb = new StringBuilder(); sb.append(USER_RECORDING_FILENAME_PREFIX).append('-'); - sb.append(mUUIDString).append('-'); + final String uuid = ResearchSettings.readResearchLoggerUuid(mPrefs); + sb.append(uuid).append('-'); sb.append(TIMESTAMP_DATEFORMAT.format(new Date())); sb.append(USER_RECORDING_FILENAME_SUFFIX); return new File(filesDir, sb.toString()); @@ -467,14 +451,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Log.w(TAG, "not in usability mode; not logging"); return; } - if (mFilesDir == null || !mFilesDir.exists()) { - Log.w(TAG, "IME storage directory does not exist. Cannot start logging."); - return; - } if (mMainLogBuffer == null) { mMainResearchLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); - mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore) { + mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, + mSuggest) { @Override protected void publish(final ArrayList<LogUnit> logUnits, boolean canIncludePrivateData) { @@ -497,7 +478,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } }; - mMainLogBuffer.setSuggest(mSuggest); } if (mFeedbackLogBuffer == null) { resetFeedbackLogging(); @@ -574,7 +554,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { if (key == null || prefs == null) { return; } @@ -596,7 +576,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang presentFeedbackDialog(latinIME); } - public void presentFeedbackDialog(LatinIME latinIME) { + public void presentFeedbackDialog(final LatinIME latinIME) { if (isMakingUserRecording()) { saveRecording(); } @@ -828,9 +808,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mPrefs == null) { return; } - final Editor e = mPrefs.edit(); - e.putString(PREF_RESEARCH_SAVED_CHANNEL, channelName); - e.apply(); + mPrefs.edit().putString(PREF_RESEARCH_SAVED_CHANNEL, channelName).apply(); } } @@ -845,10 +823,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mInFeedbackDialog = false; } - public void initSuggest(Suggest suggest) { + public void initSuggest(final Suggest suggest) { mSuggest = suggest; + // MainLogBuffer has out-of-date Suggest object. Need to close it down and create a new + // one. if (mMainLogBuffer != null) { - mMainLogBuffer.setSuggest(mSuggest); + stop(); + start(); } } @@ -1137,18 +1118,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private static String getUUID(final SharedPreferences prefs) { - String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null); - if (null == uuidString) { - UUID uuid = UUID.randomUUID(); - uuidString = uuid.toString(); - Editor editor = prefs.edit(); - editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString); - editor.apply(); - } - return uuidString; - } - private String scrubWord(String word) { final Dictionary dictionary = getDictionary(); if (dictionary == null) { @@ -1195,9 +1164,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang 0); final Integer versionCode = packageInfo.versionCode; final String versionName = packageInfo.versionName; + final String uuid = ResearchSettings.readResearchLoggerUuid(researchLogger.mPrefs); researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL, - researchLogger.mUUIDString, editorInfo.packageName, - Integer.toHexString(editorInfo.inputType), + uuid, editorInfo.packageName, Integer.toHexString(editorInfo.inputType), Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, OUTPUT_FORMAT_VERSION, IS_LOGGING_EVERYTHING, diff --git a/java/src/com/android/inputmethod/research/ResearchSettings.java b/java/src/com/android/inputmethod/research/ResearchSettings.java new file mode 100644 index 000000000..11e9ac77a --- /dev/null +++ b/java/src/com/android/inputmethod/research/ResearchSettings.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.research; + +import android.content.SharedPreferences; + +import java.util.UUID; + +public final class ResearchSettings { + public static final String PREF_RESEARCH_LOGGER_UUID = "pref_research_logger_uuid"; + public static final String PREF_RESEARCH_LOGGER_ENABLED_FLAG = + "pref_research_logger_enabled_flag"; + public static final String PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH = + "pref_research_logger_has_seen_splash"; + + private ResearchSettings() { + // Intentional empty constructor for singleton. + } + + public static String readResearchLoggerUuid(final SharedPreferences prefs) { + if (prefs.contains(PREF_RESEARCH_LOGGER_UUID)) { + return prefs.getString(PREF_RESEARCH_LOGGER_UUID, null); + } + // Generate a random string as uuid if not yet set + final String newUuid = UUID.randomUUID().toString(); + prefs.edit().putString(PREF_RESEARCH_LOGGER_UUID, newUuid).apply(); + return newUuid; + } + + public static boolean readResearchLoggerEnabledFlag(final SharedPreferences prefs) { + return prefs.getBoolean(PREF_RESEARCH_LOGGER_ENABLED_FLAG, false); + } + + public static void writeResearchLoggerEnabledFlag(final SharedPreferences prefs, + final boolean isEnabled) { + prefs.edit().putBoolean(PREF_RESEARCH_LOGGER_ENABLED_FLAG, isEnabled).apply(); + } + + public static boolean readHasSeenSplash(final SharedPreferences prefs) { + return prefs.getBoolean(PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH, false); + } + + public static void writeHasSeenSplash(final SharedPreferences prefs, + final boolean hasSeenSplash) { + prefs.edit().putBoolean(PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH, hasSeenSplash).apply(); + } +} diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 89c67fbb2..eb5bdefb7 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -56,7 +56,6 @@ public final class UploaderService extends IntentService { private static final int BUF_SIZE = 1024 * 8; protected static final int TIMEOUT_IN_MS = 1000 * 4; - private boolean mCanUpload; private File mFilesDir; private URL mUrl; @@ -68,14 +67,10 @@ public final class UploaderService extends IntentService { public void onCreate() { super.onCreate(); - mCanUpload = false; mFilesDir = null; mUrl = null; - final PackageManager packageManager = getPackageManager(); - final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET, - getPackageName()) == PackageManager.PERMISSION_GRANTED; - if (!hasPermission) { + if (!hasUploadingPermission()) { return; } @@ -86,23 +81,39 @@ public final class UploaderService extends IntentService { } mFilesDir = getFilesDir(); mUrl = new URL(urlString); - mCanUpload = true; } catch (MalformedURLException e) { e.printStackTrace(); } } + public boolean isPossibleToUpload() { + return hasUploadingPermission() && mUrl != null && !IS_INHIBITING_AUTO_UPLOAD; + } + + private boolean hasUploadingPermission() { + final PackageManager packageManager = getPackageManager(); + return packageManager.checkPermission(Manifest.permission.INTERNET, + getPackageName()) == PackageManager.PERMISSION_GRANTED; + } + @Override protected void onHandleIntent(Intent intent) { - if (!mCanUpload) { - return; + if (!isPossibleToUpload()) return; + if (isUploadingUnconditionally(intent.getExtras()) || isConvenientToUpload()) { + doUpload(); } - boolean isUploadingUnconditionally = false; - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { - isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); + } + + private boolean isUploadingUnconditionally(final Bundle bundle) { + if (bundle == null) return false; + if (bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { + return bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); } - doUpload(isUploadingUnconditionally); + return false; + } + + private boolean isConvenientToUpload() { + return isExternallyPowered() && hasWifiConnection(); } private boolean isExternallyPowered() { @@ -120,11 +131,7 @@ public final class UploaderService extends IntentService { return wifiInfo.isConnected(); } - private void doUpload(final boolean isUploadingUnconditionally) { - if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection() - || IS_INHIBITING_AUTO_UPLOAD)) { - return; - } + private void doUpload() { if (mFilesDir == null) { return; } |