From a5e564bea09b54822befff8977d971dd63303452 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Fri, 15 Feb 2013 12:54:06 -0800 Subject: [TestPrep10] Add some comments Change-Id: Ia8e0bd72b0dc09bfc256ec5d1996637554376b5f --- .../android/inputmethod/research/ResearchLog.java | 32 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 99d84938f..f033b0440 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() { @Override @@ -106,8 +119,7 @@ public class ResearchLog { } } } 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 +137,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() { @Override @@ -184,6 +202,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() { -- cgit v1.2.3-83-g751a From f170f145afa821537b2e97a02a00da96723bb84e Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Fri, 15 Feb 2013 12:56:46 -0800 Subject: [TestPrep11] Only use mHasWrittenData to control JsonWriter#endArray() call Change-Id: If2107e38575b009a4c372194ecd827e27bdbb2b4 --- java/src/com/android/inputmethod/research/ResearchLog.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index f033b0440..9016e23b3 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -107,16 +107,12 @@ 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); -- cgit v1.2.3-83-g751a From 7e2c1e69bf5782db8f358c9baeb518130aad0315 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 26 Feb 2013 17:53:05 -0800 Subject: Ensure Preference listener is unregistered Change-Id: I1a4d1e7bc45fc30cb1b63ea6020f0eee6e804333 --- java/src/com/android/inputmethod/latin/LatinIME.java | 3 +++ java/src/com/android/inputmethod/research/ResearchLogger.java | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 20209b627..2050c7663 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -561,6 +561,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/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 25633d630..b149718bf 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -322,6 +322,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mMainKeyboardView = null; } + public void onDestroy() { + if (mPrefs != null) { + mPrefs.unregisterOnSharedPreferenceChangeListener(this); + } + } + private boolean hasSeenSplash() { return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false); } -- cgit v1.2.3-83-g751a From 5ee261a99012c0ac0c230093060e8f538b1ec646 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 19 Feb 2013 15:36:40 -0800 Subject: [TestPrep17] Allow fake dictionary for testing Currently ResearchLog requires a full dictionary to perform privacy-related checks. This makes testing difficult. This change allows a fake dictionary to be used instead. Change-Id: Ifca5bd8647475a6b84e4324117e0faa0a35479ee --- .../src/com/android/inputmethod/research/MainLogBuffer.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java index 45b83dd76..9e77a1a38 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; + // TODO: Remove dependence on Suggest, and pass in Dictionary as a parameter to an appropriate + // method. private Suggest mSuggest; + @UsedForTesting + private Dictionary mDictionaryForTesting; private boolean mIsStopping = false; /* package for test */ int mNumWordsBetweenNGrams; @@ -83,7 +88,15 @@ public abstract class MainLogBuffer extends FixedLogBuffer { 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(); } -- cgit v1.2.3-83-g751a From 96d019f183fc15585a5642155b9a00e9ec3637a4 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 20 Feb 2013 11:29:55 -0800 Subject: [TestPrep18] Make class public for testing Change-Id: I4b457f44dd6a5a595c61a62061ea02c251fa9ab1 --- java/src/com/android/inputmethod/research/LogUnit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index e91976a03..c1258f496 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -45,7 +45,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; -- cgit v1.2.3-83-g751a From 5cd50faacab931959b22eda559b8a75da2d886a6 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 19 Feb 2013 16:12:44 -0800 Subject: [TestPrep20] Clean up import statement Change-Id: Ifcfec09b669d8b2012b1c960bd4b1471c7946870 --- java/src/com/android/inputmethod/research/LogUnit.java | 1 - 1 file changed, 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index c1258f496..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; -- cgit v1.2.3-83-g751a From 59912837b38f4aa41ffa1dc6ec4c7686a7fe3fdb Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 25 Feb 2013 17:13:41 -0800 Subject: [TestPrep22] Make variable package-private for testing Change-Id: I902a6a40df1c672c611672f34d2f3237b0d1821b --- java/src/com/android/inputmethod/research/ResearchLogger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b149718bf..d9f37804d 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -208,7 +208,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, -- cgit v1.2.3-83-g751a From 00ed4704f927a6bc7c675271eee8e53fc7b1200e Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 26 Feb 2013 18:00:19 -0800 Subject: Move UUID preference reading to ResearchSettings.java Change-Id: I8157249259cf8c3218c5c82a5729f4cbc1fb4eeb --- .../inputmethod/research/ResearchLogger.java | 25 ++++---------- .../inputmethod/research/ResearchSettings.java | 39 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/ResearchSettings.java (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index b149718bf..0367a15a2 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -154,7 +154,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 +161,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 @@ -249,7 +247,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } 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); @@ -413,7 +410,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang 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. @@ -431,7 +429,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()); @@ -1143,18 +1142,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) { @@ -1201,9 +1188,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..1f899ee4c --- /dev/null +++ b/java/src/com/android/inputmethod/research/ResearchSettings.java @@ -0,0 +1,39 @@ +/* + * 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"; + + 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; + } +} -- cgit v1.2.3-83-g751a From b094372bd68b2f1b1e62c48cc18503a60b6686b7 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 13 Feb 2013 16:19:01 -0800 Subject: Remove MainLogBuffer#setSuggest() multi-project commit with Ia98b1406fc2ee11a96893c77ea58d800fece7624 Change-Id: I8bc3d07b83dbe7d8fc56a618de07dade7f510b13 --- java/src/com/android/inputmethod/latin/LatinIME.java | 2 +- .../com/android/inputmethod/research/MainLogBuffer.java | 8 +++----- .../com/android/inputmethod/research/ResearchLogger.java | 15 ++++++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 79a05ceae..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; diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java index 9e77a1a38..9aa60f859 100644 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java @@ -67,7 +67,7 @@ public abstract class MainLogBuffer extends FixedLogBuffer { // TODO: Remove dependence on Suggest, and pass in Dictionary as a parameter to an appropriate // method. - private Suggest mSuggest; + private final Suggest mSuggest; @UsedForTesting private Dictionary mDictionaryForTesting; private boolean mIsStopping = false; @@ -78,13 +78,11 @@ 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; - } - - public void setSuggest(final Suggest suggest) { mSuggest = suggest; } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index d9f37804d..1899f354b 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -237,7 +237,8 @@ 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"); @@ -247,6 +248,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "IME storage directory does not exist."); } } + mSuggest = suggest; final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME); if (prefs != null) { mUUIDString = getUUID(prefs); @@ -480,7 +482,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang 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 logUnits, boolean canIncludePrivateData) { @@ -503,7 +506,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } }; - mMainLogBuffer.setSuggest(mSuggest); } if (mFeedbackLogBuffer == null) { resetFeedbackLogging(); @@ -851,10 +853,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(); } } -- cgit v1.2.3-83-g751a From 75e6fb68e91b440707b399b22fbcfcd67760a949 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Thu, 28 Feb 2013 11:18:42 -0800 Subject: Clean up PREF_USABILITY_STUDY_MODE - Refer to a common default value in DebugSettings - Make PREF_USABILITY_STUDY_MODE independent of the ResearchLogger - ResearchLogger uses its own preference through ResearchSettings multi-project commit with Ie0df836c9d779eba484b522666ec357f4e234823 Change-Id: I88547a2f619db6e7364abbbec12f9f76855dd11a --- .../com/android/inputmethod/latin/DebugSettings.java | 2 +- .../com/android/inputmethod/latin/LatinImeLogger.java | 4 ++++ .../android/inputmethod/research/ResearchLogger.java | 18 ++++-------------- .../android/inputmethod/research/ResearchSettings.java | 11 +++++++++++ 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'java/src') 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/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/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 1e1cb87b5..a402d5bb5 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -122,7 +122,6 @@ 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"; @@ -249,12 +248,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mSuggest = suggest; final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME); if (prefs != null) { - if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) { - Editor e = prefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE); - e.apply(); - } - sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false); + sIsLogging = ResearchSettings.readResearchLoggerEnabledFlag(prefs); prefs.registerOnSharedPreferenceChangeListener(this); final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L); @@ -397,13 +391,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang restart(); } - private void setLoggingAllowed(boolean enableLogging) { - if (mPrefs == null) { - return; - } - Editor e = mPrefs.edit(); - e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging); - e.apply(); + private void setLoggingAllowed(final boolean enableLogging) { + if (mPrefs == null) return; + ResearchSettings.writeResearchLoggerEnabledFlag(mPrefs, enableLogging); sIsLogging = enableLogging; } diff --git a/java/src/com/android/inputmethod/research/ResearchSettings.java b/java/src/com/android/inputmethod/research/ResearchSettings.java index 1f899ee4c..ece93a2f5 100644 --- a/java/src/com/android/inputmethod/research/ResearchSettings.java +++ b/java/src/com/android/inputmethod/research/ResearchSettings.java @@ -22,6 +22,8 @@ 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"; private ResearchSettings() { // Intentional empty constructor for singleton. @@ -36,4 +38,13 @@ public final class ResearchSettings { 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(); + } } -- cgit v1.2.3-83-g751a From 157c00145bd9d0918d7501de42b40c40ac0617c7 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Thu, 28 Feb 2013 11:43:32 -0800 Subject: Clean up logic in onUserLoggingConsent - Make ResearchSetting for whether the user has seen the splash screen - Inline #setLoggingAllowed, which is short and is now only called by onUserLoggingConsent Change-Id: Icdf4592777b80643807b6ccf1d3896459c503e02 --- .../inputmethod/research/ResearchLogger.java | 23 ++++++---------------- .../inputmethod/research/ResearchSettings.java | 11 +++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index a402d5bb5..061ae3810 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -125,7 +125,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang /* 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"; @@ -321,14 +320,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } } - private boolean hasSeenSplash() { - return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false); - } - private Dialog mSplashDialog = null; private void maybeShowSplashScreen() { - if (hasSeenSplash()) { + if (ResearchSettings.readHasSeenSplash(mPrefs)) { return; } if (mSplashDialog != null && mSplashDialog.isShowing()) { @@ -381,22 +376,16 @@ 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(final boolean enableLogging) { - if (mPrefs == null) return; - ResearchSettings.writeResearchLoggerEnabledFlag(mPrefs, enableLogging); - sIsLogging = enableLogging; - } - private static int sLogFileCounter = 0; private File createLogFile(final File filesDir) { diff --git a/java/src/com/android/inputmethod/research/ResearchSettings.java b/java/src/com/android/inputmethod/research/ResearchSettings.java index ece93a2f5..11e9ac77a 100644 --- a/java/src/com/android/inputmethod/research/ResearchSettings.java +++ b/java/src/com/android/inputmethod/research/ResearchSettings.java @@ -24,6 +24,8 @@ 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. @@ -47,4 +49,13 @@ public final class ResearchSettings { 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(); + } } -- cgit v1.2.3-83-g751a From 4eeb90cd723db5961bf597ae26f540a9a3e30970 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 26 Feb 2013 18:31:09 -0800 Subject: Clean up initialization ordering This change is based on an earlier one that got stuck in Gerrit: Iab77504b Change-Id: I27ad9dfb1bbb2300bd1e61d881a6ea0e116db066 --- .../inputmethod/research/ResearchLogger.java | 53 ++++++++++------------ 1 file changed, 24 insertions(+), 29 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 061ae3810..70e86961e 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -236,35 +236,34 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang 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."); - } - } - mSuggest = suggest; - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME); - if (prefs != null) { - sIsLogging = ResearchSettings.readResearchLoggerEnabledFlag(prefs); - 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); @@ -453,10 +452,6 @@ 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); -- cgit v1.2.3-83-g751a From 84bc16af3e2823a954b18c6607282cfe67fd2f23 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Tue, 26 Feb 2013 18:31:37 -0800 Subject: Little cleanups Change-Id: I2ded3d0a29610941fac27c9b26168dd6c06ceccc --- java/src/com/android/inputmethod/research/ResearchLogger.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 70e86961e..e705ddda1 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -267,11 +267,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang 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); } /** @@ -555,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; } @@ -577,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(); } @@ -809,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(); } } -- cgit v1.2.3-83-g751a From 25343124dd3da2edd9f3f5c84ee9e27851d6e873 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Mon, 4 Mar 2013 11:46:07 -0800 Subject: Make some methods visible for testing bug: 8302967 multi-project commit with Icf915abb57ef00840ee93b67a25db42ba51f1c38 Change-Id: I105bda5bdff871508140fb99487dddbf17992790 --- java/src/com/android/inputmethod/latin/Utils.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'java/src') 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; -- cgit v1.2.3-83-g751a From 347b2ebebf63e6958817364f7194a084aebe996f Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:06:41 -0800 Subject: [FileEncap1] Extract hasUploadingPermisssions method Change-Id: Ica1dabcf066cde6580035f12fa35a404cb32fb3f --- .../src/com/android/inputmethod/research/UploaderService.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 89c67fbb2..7acd79d63 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -72,10 +72,7 @@ public final class UploaderService extends IntentService { 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; } @@ -92,6 +89,12 @@ public final class UploaderService extends IntentService { } } + 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) { -- cgit v1.2.3-83-g751a From 466e665b3cfdaee0f6149d16d5244b54ddb86838 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:13:42 -0800 Subject: [FileEncap2] Remove mCanUpload instance variable Change-Id: I62a100c4d73bb64a14a7994f7dd9af7b6946d064 --- .../src/com/android/inputmethod/research/UploaderService.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 7acd79d63..ed195ba4b 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,7 +67,6 @@ public final class UploaderService extends IntentService { public void onCreate() { super.onCreate(); - mCanUpload = false; mFilesDir = null; mUrl = null; @@ -83,12 +81,15 @@ 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, @@ -97,9 +98,7 @@ public final class UploaderService extends IntentService { @Override protected void onHandleIntent(Intent intent) { - if (!mCanUpload) { - return; - } + if (!isPossibleToUpload()) return; boolean isUploadingUnconditionally = false; Bundle bundle = intent.getExtras(); if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { -- cgit v1.2.3-83-g751a From 7f7850d23a34f918bc44843bbc68862b45dc2752 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:17:49 -0800 Subject: [FileEncap3] Extract isUploadingUnconditionally method - remove isUploadingUnconditionally local variable Change-Id: I69be8708c1f306568cbb9c5cbefc9e42a87df497 --- .../com/android/inputmethod/research/UploaderService.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index ed195ba4b..13a98567e 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -99,12 +99,15 @@ public final class UploaderService extends IntentService { @Override protected void onHandleIntent(Intent intent) { if (!isPossibleToUpload()) return; - boolean isUploadingUnconditionally = false; - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { - isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); + doUpload(isUploadingUnconditionally(intent.getExtras())); + } + + 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 isExternallyPowered() { -- cgit v1.2.3-83-g751a From 88f466678c17bb32c274a5367da0d6138679ab6b Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:27:50 -0800 Subject: [FileEncap4] Simplify logic Change-Id: Ibb8660d12812b867856259d3e5c04a31e281027d --- java/src/com/android/inputmethod/research/UploaderService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 13a98567e..bb70c4318 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -110,6 +110,10 @@ public final class UploaderService extends IntentService { return false; } + private boolean isConvenientToUpload() { + return isExternallyPowered() && hasWifiConnection(); + } + private boolean isExternallyPowered() { final Intent intent = registerReceiver(null, new IntentFilter( Intent.ACTION_BATTERY_CHANGED)); @@ -126,10 +130,7 @@ public final class UploaderService extends IntentService { } private void doUpload(final boolean isUploadingUnconditionally) { - if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection() - || IS_INHIBITING_AUTO_UPLOAD)) { - return; - } + if (!(isUploadingUnconditionally || isConvenientToUpload())) return; if (mFilesDir == null) { return; } -- cgit v1.2.3-83-g751a From 9283644c41bf702217168a1750aa82954a4f3f47 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:32:39 -0800 Subject: [FileEncap5] Move conditional logic to caller Change-Id: I6599539059f86dc8902210d325439c08035b2e2a --- java/src/com/android/inputmethod/research/UploaderService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index bb70c4318..eb5bdefb7 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -99,7 +99,9 @@ public final class UploaderService extends IntentService { @Override protected void onHandleIntent(Intent intent) { if (!isPossibleToUpload()) return; - doUpload(isUploadingUnconditionally(intent.getExtras())); + if (isUploadingUnconditionally(intent.getExtras()) || isConvenientToUpload()) { + doUpload(); + } } private boolean isUploadingUnconditionally(final Bundle bundle) { @@ -129,8 +131,7 @@ public final class UploaderService extends IntentService { return wifiInfo.isConnected(); } - private void doUpload(final boolean isUploadingUnconditionally) { - if (!(isUploadingUnconditionally || isConvenientToUpload())) return; + private void doUpload() { if (mFilesDir == null) { return; } -- cgit v1.2.3-83-g751a From 4436446e9b173aab17c6927bb78b0f236381f7d1 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 12:50:11 -0800 Subject: [FileEncap6] Extract Uploader class Gerrit was stuck on original change I9ef4eb7450 Change-Id: I6d4ee686944074779aca5b09b018782c7033874d --- .../com/android/inputmethod/research/Uploader.java | 187 +++++++++++++++++++++ .../inputmethod/research/UploaderService.java | 172 ++----------------- 2 files changed, 197 insertions(+), 162 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/Uploader.java (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java new file mode 100644 index 000000000..14e46cd34 --- /dev/null +++ b/java/src/com/android/inputmethod/research/Uploader.java @@ -0,0 +1,187 @@ +/* + * 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.Manifest; +import android.app.AlarmManager; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.BatteryManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.define.ProductionFlag; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Manages the uploading of ResearchLog files. + */ +public final class Uploader { + private static final String TAG = Uploader.class.getSimpleName(); + private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; + // Set IS_INHIBITING_AUTO_UPLOAD to true for local testing + private static final boolean IS_INHIBITING_AUTO_UPLOAD = false + && ProductionFlag.IS_EXPERIMENTAL_DEBUG; // Force false for non-debug builds + private static final int BUF_SIZE = 1024 * 8; + + private final Context mContext; + private final File mFilesDir; + private final URL mUrl; + + public Uploader(final Context context) { + mContext = context; + mFilesDir = context.getFilesDir(); + + final String urlString = context.getString(R.string.research_logger_upload_url); + if (TextUtils.isEmpty(urlString)) { + mUrl = null; + return; + } + URL url = null; + try { + url = new URL(urlString); + } catch (final MalformedURLException e) { + Log.e(TAG, "Bad URL for uploading", e); + } + mUrl = url; + } + + public boolean isPossibleToUpload() { + return hasUploadingPermission() && mUrl != null && !IS_INHIBITING_AUTO_UPLOAD; + } + + private boolean hasUploadingPermission() { + final PackageManager packageManager = mContext.getPackageManager(); + return packageManager.checkPermission(Manifest.permission.INTERNET, + mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED; + } + + public boolean isConvenientToUpload() { + return isExternallyPowered() && hasWifiConnection(); + } + + private boolean isExternallyPowered() { + final Intent intent = mContext.registerReceiver(null, new IntentFilter( + Intent.ACTION_BATTERY_CHANGED)); + final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + return pluggedState == BatteryManager.BATTERY_PLUGGED_AC + || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; + } + + private boolean hasWifiConnection() { + final ConnectivityManager manager = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo.isConnected(); + } + + public void doUpload() { + if (mFilesDir == null) { + return; + } + final File[] files = mFilesDir.listFiles(new FileFilter() { + @Override + public boolean accept(final File pathname) { + return pathname.getName().startsWith(ResearchLogger.LOG_FILENAME_PREFIX) + && !pathname.canWrite(); + } + }); + // TODO: Remove local variable + boolean success = true; + if (files.length == 0) { + success = false; + } + for (final File file : files) { + if (!uploadFile(file)) { + success = false; + } + } + } + + private boolean uploadFile(final File file) { + if (DEBUG) { + Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); + } + boolean success = false; + final int contentLength = (int) file.length(); + HttpURLConnection connection = null; + InputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(file); + connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(contentLength); + final OutputStream outputStream = connection.getOutputStream(); + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = fileInputStream.read(buf)) != -1) { + outputStream.write(buf, 0, numBytesRead); + if (DEBUG) { + Log.d(TAG, new String(buf)); + } + } + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + Log.d(TAG, "upload failed: " + connection.getResponseCode()); + final InputStream netInputStream = connection.getInputStream(); + final BufferedReader reader = new BufferedReader(new InputStreamReader( + netInputStream)); + String line; + while ((line = reader.readLine()) != null) { + Log.d(TAG, "| " + reader.readLine()); + } + reader.close(); + return success; + } + file.delete(); + success = true; + if (DEBUG) { + Log.d(TAG, "upload successful"); + } + } catch (final IOException e) { + Log.e(TAG, "Exception uploading file", e); + } finally { + if (fileInputStream != null) { + try { + fileInputStream.close(); + } catch (final IOException e) { + Log.e(TAG, "Exception closing uploaded file", e); + } + } + if (connection != null) { + connection.disconnect(); + } + } + return success; + } +} diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index eb5bdefb7..26b651056 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -16,91 +16,36 @@ package com.android.inputmethod.research; -import android.Manifest; import android.app.AlarmManager; import android.app.IntentService; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.BatteryManager; import android.os.Bundle; -import android.util.Log; -import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.define.ProductionFlag; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; - +/** + * Service to invoke the uploader. + * + * Can be regularly invoked, invoked on boot, etc. + */ public final class UploaderService extends IntentService { private static final String TAG = UploaderService.class.getSimpleName(); private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; - // Set IS_INHIBITING_AUTO_UPLOAD to true for local testing - private static final boolean IS_INHIBITING_AUTO_UPLOAD = false - && ProductionFlag.IS_EXPERIMENTAL_DEBUG; // Force false in production public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; public static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() + ".extra.UPLOAD_UNCONDITIONALLY"; - private static final int BUF_SIZE = 1024 * 8; protected static final int TIMEOUT_IN_MS = 1000 * 4; - private File mFilesDir; - private URL mUrl; - public UploaderService() { super("Research Uploader Service"); } @Override - public void onCreate() { - super.onCreate(); - - mFilesDir = null; - mUrl = null; - - if (!hasUploadingPermission()) { - return; - } - - try { - final String urlString = getString(R.string.research_logger_upload_url); - if (urlString == null || urlString.equals("")) { - return; - } - mFilesDir = getFilesDir(); - mUrl = new URL(urlString); - } 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 (!isPossibleToUpload()) return; - if (isUploadingUnconditionally(intent.getExtras()) || isConvenientToUpload()) { - doUpload(); + protected void onHandleIntent(final Intent intent) { + final Uploader uploader = new Uploader(this); + if (!uploader.isPossibleToUpload()) return; + if (isUploadingUnconditionally(intent.getExtras()) || uploader.isConvenientToUpload()) { + uploader.doUpload(); } } @@ -111,101 +56,4 @@ public final class UploaderService extends IntentService { } return false; } - - private boolean isConvenientToUpload() { - return isExternallyPowered() && hasWifiConnection(); - } - - private boolean isExternallyPowered() { - final Intent intent = registerReceiver(null, new IntentFilter( - Intent.ACTION_BATTERY_CHANGED)); - final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - return pluggedState == BatteryManager.BATTERY_PLUGGED_AC - || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; - } - - private boolean hasWifiConnection() { - final ConnectivityManager manager = - (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - return wifiInfo.isConnected(); - } - - private void doUpload() { - if (mFilesDir == null) { - return; - } - final File[] files = mFilesDir.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.getName().startsWith(ResearchLogger.LOG_FILENAME_PREFIX) - && !pathname.canWrite(); - } - }); - boolean success = true; - if (files.length == 0) { - success = false; - } - for (final File file : files) { - if (!uploadFile(file)) { - success = false; - } - } - } - - private boolean uploadFile(File file) { - if (DEBUG) { - Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); - } - boolean success = false; - final int contentLength = (int) file.length(); - HttpURLConnection connection = null; - InputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(file); - connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("PUT"); - connection.setDoOutput(true); - connection.setFixedLengthStreamingMode(contentLength); - final OutputStream os = connection.getOutputStream(); - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = fileInputStream.read(buf)) != -1) { - os.write(buf, 0, numBytesRead); - if (DEBUG) { - Log.d(TAG, new String(buf)); - } - } - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.d(TAG, "upload failed: " + connection.getResponseCode()); - InputStream netInputStream = connection.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream)); - String line; - while ((line = reader.readLine()) != null) { - Log.d(TAG, "| " + reader.readLine()); - } - reader.close(); - return success; - } - file.delete(); - success = true; - if (DEBUG) { - Log.d(TAG, "upload successful"); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (connection != null) { - connection.disconnect(); - } - } - return success; - } } -- cgit v1.2.3-83-g751a From ee3261ff558ee734e15af5424f98bb8d8776795f Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 14:12:50 -0800 Subject: [FileEncap7] Extract uploadContents method Change-Id: I018b2726533042dbcb25713ea039aec41c148db2 --- .../com/android/inputmethod/research/Uploader.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java index 14e46cd34..ac64076cb 100644 --- a/java/src/com/android/inputmethod/research/Uploader.java +++ b/java/src/com/android/inputmethod/research/Uploader.java @@ -143,14 +143,7 @@ public final class Uploader { connection.setDoOutput(true); connection.setFixedLengthStreamingMode(contentLength); final OutputStream outputStream = connection.getOutputStream(); - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = fileInputStream.read(buf)) != -1) { - outputStream.write(buf, 0, numBytesRead); - if (DEBUG) { - Log.d(TAG, new String(buf)); - } - } + uploadContents(fileInputStream, outputStream); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { Log.d(TAG, "upload failed: " + connection.getResponseCode()); final InputStream netInputStream = connection.getInputStream(); @@ -184,4 +177,14 @@ public final class Uploader { } return success; } + + private static void uploadContents(final InputStream is, final OutputStream os) + throws IOException { + // TODO: Switch to NIO. + final byte[] buf = new byte[BUF_SIZE]; + int numBytesRead; + while ((numBytesRead = is.read(buf)) != -1) { + os.write(buf, 0, numBytesRead); + } + } } -- cgit v1.2.3-83-g751a From bba39b9b678f4fb00511ba88c12eef9082ecc628 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 14:13:29 -0800 Subject: [FileEncap8] Remove useless "success" variable Change-Id: Icf76bc0b795b6a9221d9a6d61c38df31af54ab7b --- java/src/com/android/inputmethod/research/Uploader.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java index ac64076cb..df495a88d 100644 --- a/java/src/com/android/inputmethod/research/Uploader.java +++ b/java/src/com/android/inputmethod/research/Uploader.java @@ -116,23 +116,15 @@ public final class Uploader { && !pathname.canWrite(); } }); - // TODO: Remove local variable - boolean success = true; - if (files.length == 0) { - success = false; - } for (final File file : files) { - if (!uploadFile(file)) { - success = false; - } + uploadFile(file); } } - private boolean uploadFile(final File file) { + private void uploadFile(final File file) { if (DEBUG) { Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); } - boolean success = false; final int contentLength = (int) file.length(); HttpURLConnection connection = null; InputStream fileInputStream = null; @@ -154,10 +146,9 @@ public final class Uploader { Log.d(TAG, "| " + reader.readLine()); } reader.close(); - return success; + return; } file.delete(); - success = true; if (DEBUG) { Log.d(TAG, "upload successful"); } @@ -175,7 +166,6 @@ public final class Uploader { connection.disconnect(); } } - return success; } private static void uploadContents(final InputStream is, final OutputStream os) -- cgit v1.2.3-83-g751a From 220b271dfa59eeeeba6771175934464a48ac5466 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 14 Feb 2013 14:18:55 -0800 Subject: [Lazy4] Remove useless debug code This code is cluttering readability and getting in the way of future updates. Future updates will make it much easier to debug and to test this code, so this needs to disappear. Change-Id: I32e28ec608587b6b7c07250a2692b13e8fc98465 --- .../com/android/inputmethod/research/LogUnit.java | 33 ---------------------- 1 file changed, 33 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java index 839e2b7ba..61b6df0f4 100644 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ b/java/src/com/android/inputmethod/research/LogUnit.java @@ -119,22 +119,6 @@ public class LogUnit { */ public synchronized void publishTo(final ResearchLog researchLog, final boolean canIncludePrivateData) { - // Prepare debugging output if necessary - final StringWriter debugStringWriter; - final JsonWriter debugJsonWriter; - if (DEBUG) { - debugStringWriter = new StringWriter(); - debugJsonWriter = new JsonWriter(debugStringWriter); - debugJsonWriter.setIndent(" "); - try { - debugJsonWriter.beginArray(); - } catch (IOException e) { - Log.e(TAG, "Could not open array in JsonWriter", e); - } - } else { - debugStringWriter = null; - debugJsonWriter = null; - } // Write out any logStatement that passes the privacy filter. final int size = mLogStatementList.size(); if (size != 0) { @@ -157,29 +141,12 @@ public class LogUnit { outputLogUnitStart(jsonWriter, canIncludePrivateData); } logStatement.outputToLocked(jsonWriter, mTimeList.get(i), mValuesList.get(i)); - if (DEBUG) { - logStatement.outputToLocked(debugJsonWriter, mTimeList.get(i), - mValuesList.get(i)); - } } if (jsonWriter != null) { // We must have called logUnitStart earlier, so emit a logUnitStop. outputLogUnitStop(jsonWriter); } } - if (DEBUG) { - try { - debugJsonWriter.endArray(); - debugJsonWriter.flush(); - } catch (IOException e) { - Log.e(TAG, "Could not close array in JsonWriter", e); - } - final String bigString = debugStringWriter.getBuffer().toString(); - final String[] lines = bigString.split("\n"); - for (String line : lines) { - Log.d(TAG, line); - } - } } private static final String WORD_KEY = "_wo"; -- cgit v1.2.3-83-g751a From 7423005b96b3afc3a6ff55ae40d143920d5a6221 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 14 Feb 2013 14:03:24 -0800 Subject: [Lazy1] Switch to blocking log closures Change-Id: I4daec20b7b47b0d71c5aab6e17cd660015e19e71 --- .../android/inputmethod/research/ResearchLog.java | 47 +++++++++++++++------- .../inputmethod/research/ResearchLogger.java | 40 ++++++------------ 2 files changed, 45 insertions(+), 42 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 9016e23b3..6335b195f 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -20,11 +20,11 @@ import android.content.Context; import android.util.JsonWriter; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.define.ProductionFlag; import java.io.BufferedWriter; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -54,7 +54,6 @@ public class ResearchLog { 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; - private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4; /* package */ final ScheduledExecutorService mExecutor; /* package */ final File mFile; @@ -100,7 +99,7 @@ public class ResearchLog { * * See class comment for details about {@code JsonWriter} construction. */ - public synchronized void close(final Runnable onClosed) { + private synchronized void close(final Runnable onClosed) { mExecutor.submit(new Callable() { @Override public Object call() throws Exception { @@ -131,15 +130,22 @@ public class ResearchLog { mExecutor.shutdown(); } - private boolean mIsAbortSuccessful; - /** - * Waits for publication requests to finish, closes the {@link JsonWriter}, but then deletes the - * backing file used for output. + * Block until the research log has shut down and spooled out all output or {@code timeout} + * occurs. * - * See class comment for details about {@code JsonWriter} construction. + * @param timeout time to wait for close in milliseconds + */ + public void blockingClose(final long timeout) { + close(null); + awaitTermination(timeout, TimeUnit.MILLISECONDS); + } + + /** + * Waits for publication requests to finish, closes the JsonWriter, but then deletes the backing + * output file. */ - public synchronized void abort() { + private synchronized void abort() { mExecutor.submit(new Callable() { @Override public Object call() throws Exception { @@ -151,7 +157,7 @@ public class ResearchLog { } } finally { if (mFile != null) { - mIsAbortSuccessful = mFile.delete(); + mFile.delete(); } } return null; @@ -161,14 +167,25 @@ public class ResearchLog { mExecutor.shutdown(); } - public boolean blockingAbort() throws InterruptedException { + /** + * Block until the research log has aborted or {@code timeout} occurs. + * + * @param timeout time to wait for close in milliseconds + */ + public void blockingAbort(final long timeout) { abort(); - mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - return mIsAbortSuccessful; + awaitTermination(timeout, TimeUnit.MILLISECONDS); } - public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException { - mExecutor.awaitTermination(delay, timeUnit); + @UsedForTesting + public void awaitTermination(final long delay, final TimeUnit timeUnit) { + try { + if (!mExecutor.awaitTermination(delay, timeUnit)) { + Log.e(TAG, "ResearchLog executor timed out while awaiting terminaion"); + } + } catch (final InterruptedException e) { + Log.e(TAG, "ResearchLog executor interrupted while awaiting terminaion", e); + } } /* package */ synchronized void flush() { diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index e705ddda1..11d1a5222 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -154,6 +154,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 private static final String PREF_RESEARCH_SAVED_CHANNEL = "pref_research_saved_channel"; + private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = 5 * 1000; + private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = 5 * 1000; + private static final ResearchLogger sInstance = new ResearchLogger(); private static String sAccountType = null; private static String sAllowedAccountDomain = null; @@ -502,42 +505,29 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang commitCurrentLogUnit(); mMainLogBuffer.setIsStopping(); mMainLogBuffer.shiftAndPublishAll(); - mMainResearchLog.close(null /* callback */); + mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); mMainLogBuffer = null; } if (mFeedbackLogBuffer != null) { - mFeedbackLog.close(null /* callback */); + mFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); mFeedbackLogBuffer = null; } } - public boolean abort() { + public void abort() { if (DEBUG) { Log.d(TAG, "abort called"); } - boolean didAbortMainLog = false; if (mMainLogBuffer != null) { mMainLogBuffer.clear(); - try { - didAbortMainLog = mMainResearchLog.blockingAbort(); - } catch (InterruptedException e) { - // Don't know whether this succeeded or not. We assume not; this is reported - // to the caller. - } + mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); mMainLogBuffer = null; } - boolean didAbortFeedbackLog = false; if (mFeedbackLogBuffer != null) { mFeedbackLogBuffer.clear(); - try { - didAbortFeedbackLog = mFeedbackLog.blockingAbort(); - } catch (InterruptedException e) { - // Don't know whether this succeeded or not. We assume not; this is reported - // to the caller. - } + mFeedbackLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); mFeedbackLogBuffer = null; } - return didAbortMainLog && didAbortFeedbackLog; } private void restart() { @@ -620,7 +610,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void startRecordingInternal() { if (mUserRecordingLog != null) { - mUserRecordingLog.abort(); + mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); } mUserRecordingFile = createUserRecordingFile(mFilesDir); mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME); @@ -658,7 +648,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void cancelRecording() { if (mUserRecordingLog != null) { - mUserRecordingLog.abort(); + mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); } mUserRecordingLog = null; mUserRecordingLogBuffer = null; @@ -670,7 +660,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void saveRecording() { commitCurrentLogUnit(); publishLogBuffer(mUserRecordingLogBuffer, mUserRecordingLog, true); - mUserRecordingLog.close(null); + mUserRecordingLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); mUserRecordingLog = null; mUserRecordingLogBuffer = null; @@ -782,12 +772,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang feedbackContents, accountName, recording); mFeedbackLogBuffer.shiftIn(feedbackLogUnit); publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */); - mSavedFeedbackLog.close(new Runnable() { - @Override - public void run() { - uploadNow(); - } - }); + mSavedFeedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); + uploadNow(); if (isIncludingRecording && DEBUG_REPLAY_AFTER_FEEDBACK) { final Handler handler = new Handler(); -- cgit v1.2.3-83-g751a From 13a82d95306d9d5662fc11cfa5269cd86164bc3a Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 14 Feb 2013 14:14:48 -0800 Subject: [Lazy2] Pass a runnable to abort Change-Id: I721afbf426099753aa876a55323eb806b08c24d6 --- .../src/com/android/inputmethod/research/ResearchLog.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 6335b195f..f7c5fd50e 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -98,6 +98,8 @@ public class ResearchLog { * output. * * See class comment for details about {@code JsonWriter} construction. + * + * @param onClosed run after the close() operation has completed asynchronously */ private synchronized void close(final Runnable onClosed) { mExecutor.submit(new Callable() { @@ -144,8 +146,10 @@ public class ResearchLog { /** * Waits for publication requests to finish, closes the JsonWriter, but then deletes the backing * output file. + * + * @param onAbort run after the abort() operation has completed asynchronously */ - private synchronized void abort() { + private synchronized void abort(final Runnable onAbort) { mExecutor.submit(new Callable() { @Override public Object call() throws Exception { @@ -159,6 +163,9 @@ public class ResearchLog { if (mFile != null) { mFile.delete(); } + if (onAbort != null) { + onAbort.run(); + } } return null; } @@ -173,7 +180,7 @@ public class ResearchLog { * @param timeout time to wait for close in milliseconds */ public void blockingAbort(final long timeout) { - abort(); + abort(null); awaitTermination(timeout, TimeUnit.MILLISECONDS); } @@ -231,10 +238,10 @@ public class ResearchLog { return null; } }); - } catch (RejectedExecutionException e) { + } catch (final RejectedExecutionException e) { // TODO: Add code to record loss of data, and report. if (DEBUG) { - Log.d(TAG, "ResearchLog.publish() rejecting scheduled execution"); + Log.d(TAG, "ResearchLog.publish() rejecting scheduled execution", e); } } } -- cgit v1.2.3-83-g751a From f33f1cab2fd57fe39955593e68902554ed57a053 Mon Sep 17 00:00:00 2001 From: Kurt Partridge Date: Wed, 27 Feb 2013 14:40:54 -0800 Subject: [FileEncap9] Extract ResearchLogDirectory class Previously used a monotonically increasing int. Now uses uuid and nanoseconds. squashed in: [FileEncap11] Read preference from ResearchSettings Change-Id: Ic779e0a69db6b16e92c6f4b63dbe7b7add566ab6 [FileEncap12] Simplify directory cleanup invocation Change-Id: I688047409c0343d32b11447fb625dfb726c731ec [FileEncap14] Change log filename syntax Change-Id: I9243b20b2eb392f81ab8c5c3d19315211240e0bc Change-Id: I5c9d70e0cb7b0965158e17dd71dfab796bd9a440 --- .../android/inputmethod/research/ResearchLog.java | 2 + .../inputmethod/research/ResearchLogDirectory.java | 111 +++++++++++++++++++++ .../inputmethod/research/ResearchLogger.java | 93 +++++------------ .../inputmethod/research/ResearchSettings.java | 11 ++ .../com/android/inputmethod/research/Uploader.java | 18 +--- 5 files changed, 153 insertions(+), 82 deletions(-) create mode 100644 java/src/com/android/inputmethod/research/ResearchLogDirectory.java (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index f7c5fd50e..080366e9b 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -118,6 +118,8 @@ public class ResearchLog { } catch (Exception e) { Log.d(TAG, "error when closing ResearchLog:", e); } finally { + // Marking the file as read-only signals that this log file is ready to be + // uploaded. if (mFile != null && mFile.exists()) { mFile.setWritable(false, false); } diff --git a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java new file mode 100644 index 000000000..291dea5d0 --- /dev/null +++ b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java @@ -0,0 +1,111 @@ +/* + * 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.Context; +import android.util.Log; + +import java.io.File; +import java.io.FileFilter; + +/** + * Manages log files. + * + * This class handles all aspects where and how research log data is stored. This includes + * generating log filenames in the correct place with the correct names, and cleaning up log files + * under this directory. + */ +public class ResearchLogDirectory { + public static final String TAG = ResearchLogDirectory.class.getSimpleName(); + /* package */ static final String LOG_FILENAME_PREFIX = "researchLog"; + private static final String FILENAME_SUFFIX = ".txt"; + private static final String USER_RECORDING_FILENAME_PREFIX = "recording"; + + private static final ReadOnlyLogFileFilter sUploadableLogFileFilter = + new ReadOnlyLogFileFilter(); + + private final File mFilesDir; + + static class ReadOnlyLogFileFilter implements FileFilter { + @Override + public boolean accept(final File pathname) { + return pathname.getName().startsWith(ResearchLogDirectory.LOG_FILENAME_PREFIX) + && !pathname.canWrite(); + } + } + + /** + * Creates a new ResearchLogDirectory, creating the storage directory if it does not exist. + */ + public ResearchLogDirectory(final Context context) { + mFilesDir = getLoggingDirectory(context); + if (mFilesDir == null) { + throw new NullPointerException("No files directory specified"); + } + if (!mFilesDir.exists()) { + mFilesDir.mkdirs(); + } + } + + private File getLoggingDirectory(final Context context) { + // TODO: Switch to using a subdirectory of getFilesDir(). + return context.getFilesDir(); + } + + /** + * Get an array of log files that are ready for uploading. + * + * A file is ready for uploading if it is marked as read-only. + * + * @return the array of uploadable files + */ + public File[] getUploadableLogFiles() { + try { + return mFilesDir.listFiles(sUploadableLogFileFilter); + } catch (final SecurityException e) { + Log.e(TAG, "Could not cleanup log directory, permission denied", e); + return new File[0]; + } + } + + public void cleanupLogFilesOlderThan(final long time) { + try { + for (final File file : mFilesDir.listFiles()) { + final String filename = file.getName(); + if ((filename.startsWith(LOG_FILENAME_PREFIX) + || filename.startsWith(USER_RECORDING_FILENAME_PREFIX)) + && (file.lastModified() < time)) { + file.delete(); + } + } + } catch (final SecurityException e) { + Log.e(TAG, "Could not cleanup log directory, permission denied", e); + } + } + + public File getLogFilePath(final long time) { + return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time)); + } + + public File getUserRecordingFilePath(final long time) { + return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time)); + } + + private static String getUniqueFilename(final String prefix, final long time) { + return prefix + "-" + time + FILENAME_SUFFIX; + } +} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 11d1a5222..e932a2d91 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -125,12 +125,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang /* package */ static boolean sIsLogging = false; private static final int OUTPUT_FORMAT_VERSION = 5; private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - /* 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"; - private static final String USER_RECORDING_FILENAME_SUFFIX = ".txt"; - private static final SimpleDateFormat TIMESTAMP_DATEFORMAT = - new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US); // Whether all words should be recorded, leaving unsampled word between bigrams. Useful for // testing. /* package for test */ static final boolean IS_LOGGING_EVERYTHING = false @@ -156,12 +150,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = 5 * 1000; private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = 5 * 1000; + private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS; + private static final long MAX_LOGFILE_AGE_IN_MS = 4 * DateUtils.DAY_IN_MILLIS; private static final ResearchLogger sInstance = new ResearchLogger(); private static String sAccountType = null; private static String sAllowedAccountDomain = null; - // to write to a different filename, e.g., for testing, set mFile before calling start() - /* package */ File mFilesDir; /* 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 @@ -187,9 +181,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area" // U+E001 is in the "private-use area" /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001"; - 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 = 4 * DateUtils.DAY_IN_MILLIS; protected static final int SUSPEND_DURATION_IN_MINUTES = 1; // set when LatinIME should ignore an onUpdateSelection() callback that // arises from operations in this class @@ -203,6 +194,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private final Statistics mStatistics; private final MotionEventReader mMotionEventReader = new MotionEventReader(); private final Replayer mReplayer = Replayer.getInstance(); + private ResearchLogDirectory mResearchLogDirectory; private Intent mUploadIntent; private Intent mUploadNowIntent; @@ -240,11 +232,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final Suggest suggest) { assert latinIME != null; 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); @@ -256,15 +243,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang sAccountType = res.getString(R.string.research_account_type); sAllowedAccountDomain = res.getString(R.string.research_allowed_account_domain); - // 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 directory manager + mResearchLogDirectory = new ResearchLogDirectory(mLatinIME); + cleanLogDirectoryIfNeeded(mResearchLogDirectory, System.currentTimeMillis()); // Initialize external services mUploadIntent = new Intent(mLatinIME, UploaderService.class); @@ -276,6 +257,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mReplayer.setKeyboardSwitcher(keyboardSwitcher); } + private void cleanLogDirectoryIfNeeded(final ResearchLogDirectory researchLogDirectory, + final long now) { + final long lastCleanupTime = ResearchSettings.readResearchLastDirCleanupTime(mPrefs); + if (now - lastCleanupTime < DURATION_BETWEEN_DIR_CLEANUP_IN_MS) return; + final long oldestAllowedFileTime = now - MAX_LOGFILE_AGE_IN_MS; + mResearchLogDirectory.cleanupLogFilesOlderThan(oldestAllowedFileTime); + ResearchSettings.writeResearchLastDirCleanupTime(mPrefs, now); + } + /** * Arrange for the UploaderService to be run on a regular basis. * @@ -295,17 +285,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent); } - private void cleanupLoggingDir(final File dir, final long time) { - for (File file : dir.listFiles()) { - final String filename = file.getName(); - if ((filename.startsWith(ResearchLogger.LOG_FILENAME_PREFIX) - || filename.startsWith(ResearchLogger.USER_RECORDING_FILENAME_PREFIX)) - && file.lastModified() < time) { - file.delete(); - } - } - } - public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) { mMainKeyboardView = mainKeyboardView; maybeShowSplashScreen(); @@ -387,35 +366,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang restart(); } - private static int sLogFileCounter = 0; - - private File createLogFile(final File filesDir) { - final StringBuilder sb = new StringBuilder(); - sb.append(LOG_FILENAME_PREFIX).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. - if (sLogFileCounter < Integer.MAX_VALUE) { - sLogFileCounter++; - } else { - // Wrap the counter, in the unlikely event of overflow. - sLogFileCounter = 0; - } - sb.append(sLogFileCounter); - sb.append(LOG_FILENAME_SUFFIX); - return new File(filesDir, sb.toString()); - } - - private File createUserRecordingFile(final File filesDir) { - final StringBuilder sb = new StringBuilder(); - sb.append(USER_RECORDING_FILENAME_PREFIX).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()); + private void setLoggingAllowed(final boolean enableLogging) { + if (mPrefs == null) return; + sIsLogging = enableLogging; + ResearchSettings.writeResearchLoggerEnabledFlag(mPrefs, enableLogging); } private void checkForEmptyEditor() { @@ -455,7 +409,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang return; } if (mMainLogBuffer == null) { - mMainResearchLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); + mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( + System.currentTimeMillis()), mLatinIME); final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, mSuggest) { @@ -488,7 +443,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } private void resetFeedbackLogging() { - mFeedbackLog = new ResearchLog(createLogFile(mFilesDir), mLatinIME); + mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( + System.currentTimeMillis()), mLatinIME); mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); } @@ -612,7 +568,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang if (mUserRecordingLog != null) { mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); } - mUserRecordingFile = createUserRecordingFile(mFilesDir); + mUserRecordingFile = mResearchLogDirectory.getUserRecordingFilePath( + System.currentTimeMillis()); mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME); mUserRecordingLogBuffer = new LogBuffer(); resetRecordingTimer(); diff --git a/java/src/com/android/inputmethod/research/ResearchSettings.java b/java/src/com/android/inputmethod/research/ResearchSettings.java index 11e9ac77a..c0bc03fde 100644 --- a/java/src/com/android/inputmethod/research/ResearchSettings.java +++ b/java/src/com/android/inputmethod/research/ResearchSettings.java @@ -26,6 +26,8 @@ public final class ResearchSettings { "pref_research_logger_enabled_flag"; public static final String PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH = "pref_research_logger_has_seen_splash"; + public static final String PREF_RESEARCH_LAST_DIR_CLEANUP_TIME = + "pref_research_last_dir_cleanup_time"; private ResearchSettings() { // Intentional empty constructor for singleton. @@ -58,4 +60,13 @@ public final class ResearchSettings { final boolean hasSeenSplash) { prefs.edit().putBoolean(PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH, hasSeenSplash).apply(); } + + public static long readResearchLastDirCleanupTime(final SharedPreferences prefs) { + return prefs.getLong(PREF_RESEARCH_LAST_DIR_CLEANUP_TIME, 0L); + } + + public static void writeResearchLastDirCleanupTime(final SharedPreferences prefs, + final long lastDirCleanupTime) { + prefs.edit().putLong(PREF_RESEARCH_LAST_DIR_CLEANUP_TIME, lastDirCleanupTime).apply(); + } } diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java index df495a88d..152b94d30 100644 --- a/java/src/com/android/inputmethod/research/Uploader.java +++ b/java/src/com/android/inputmethod/research/Uploader.java @@ -17,7 +17,6 @@ package com.android.inputmethod.research; import android.Manifest; -import android.app.AlarmManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -33,7 +32,6 @@ import com.android.inputmethod.latin.define.ProductionFlag; import java.io.BufferedReader; import java.io.File; -import java.io.FileFilter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -55,12 +53,12 @@ public final class Uploader { private static final int BUF_SIZE = 1024 * 8; private final Context mContext; - private final File mFilesDir; + private final ResearchLogDirectory mResearchLogDirectory; private final URL mUrl; public Uploader(final Context context) { mContext = context; - mFilesDir = context.getFilesDir(); + mResearchLogDirectory = new ResearchLogDirectory(context); final String urlString = context.getString(R.string.research_logger_upload_url); if (TextUtils.isEmpty(urlString)) { @@ -106,16 +104,8 @@ public final class Uploader { } public void doUpload() { - if (mFilesDir == null) { - return; - } - final File[] files = mFilesDir.listFiles(new FileFilter() { - @Override - public boolean accept(final File pathname) { - return pathname.getName().startsWith(ResearchLogger.LOG_FILENAME_PREFIX) - && !pathname.canWrite(); - } - }); + final File[] files = mResearchLogDirectory.getUploadableLogFiles(); + if (files == null) return; for (final File file : files) { uploadFile(file); } -- cgit v1.2.3-83-g751a From a462cd80ae9d07cea596c61c54d7b1b2ba6c6645 Mon Sep 17 00:00:00 2001 From: Satoshi Kataoka Date: Tue, 19 Mar 2013 16:49:19 +0900 Subject: Support feedback Bug: 8143554 Change-Id: If0a6bafc94da040f97f27c6d046fb58bed9fd615 --- java/res/values/strings.xml | 2 ++ java/res/xml/prefs.xml | 3 +++ .../android/inputmethod/latin/FeedbackUtils.java | 28 ++++++++++++++++++++++ .../com/android/inputmethod/latin/Settings.java | 2 ++ .../inputmethod/latin/SettingsFragment.java | 21 ++++++++++++++++ 5 files changed, 56 insertions(+) create mode 100644 java/src/com/android/inputmethod/latin/FeedbackUtils.java (limited to 'java/src') diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 03dce9ca6..273525100 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -308,6 +308,8 @@ - operation[CHAR LIMIT=100] --> Please demonstrate the issue you are writing about.\n\nWhen finished, select the \"Bug?\" button again." + + Send feedback Recording cancelled due to timeout diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 51f580721..e299ce4f8 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -176,6 +176,9 @@ android:key="pref_show_setup_wizard_icon" android:title="@string/show_setup_wizard_icon" /> + Date: Tue, 19 Mar 2013 17:02:14 -0700 Subject: am 9da7fa0f: am 559616fb: Prevent keyboard A11y proxy from referencing a null keyboard view. * commit '9da7fa0f50fcc1bbbce648940c0f6849d9e6624c': Prevent keyboard A11y proxy from referencing a null keyboard view. --- .../accessibility/AccessibleKeyboardViewProxy.java | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'java/src') diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index e6b44120f..c0028e4cf 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -110,6 +110,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * enabled. */ public void setKeyboard() { + if (mView == null) { + return; + } + if (mAccessibilityNodeProvider != null) { mAccessibilityNodeProvider.setKeyboard(); } @@ -125,6 +129,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * Called when the keyboard is hidden and accessibility is enabled. */ public void onHideWindow() { + if (mView == null) { + return; + } + announceKeyboardHidden(); } @@ -179,10 +187,15 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual * node hierarchy provider. * + * @param host The host view for the provider. * @return The accessibility node provider for the current keyboard. */ @Override public AccessibilityEntityProvider getAccessibilityNodeProvider(final View host) { + if (mView == null) { + return null; + } + return getAccessibilityNodeProvider(); } @@ -206,6 +219,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * @return {@code true} if the event is handled */ public boolean dispatchHoverEvent(final MotionEvent event, final PointerTracker tracker) { + if (mView == null) { + return false; + } + final int x = (int) event.getX(); final int y = (int) event.getY(); final Key previousKey = mLastHoverKey; @@ -326,6 +343,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * Notifies the user of changes in the keyboard shift state. */ public void notifyShiftState() { + if (mView == null) { + return; + } + final Keyboard keyboard = mView.getKeyboard(); final KeyboardId keyboardId = keyboard.mId; final int elementId = keyboardId.mElementId; @@ -352,6 +373,10 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp * Notifies the user of changes in the keyboard symbols state. */ public void notifySymbolsState() { + if (mView == null) { + return; + } + final Keyboard keyboard = mView.getKeyboard(); final Context context = mView.getContext(); final KeyboardId keyboardId = keyboard.mId; -- cgit v1.2.3-83-g751a