diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
index 2a90ba2a0..2725e7f49 100644
--- a/java/res/layout/research_feedback_fragment_layout.xml
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -14,107 +14,111 @@
limitations under the License.
-->
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:layout_marginBottom="8dip"
+ android:checked="false"
+ android:text="@string/research_feedback_include_recording_label" />
-
-
+
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index e3cd84c9d..8822e8d18 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -278,6 +278,9 @@
Include account name
+
+
+ Include recorded demonstrationEnter your feedback here.
@@ -287,6 +290,13 @@
Cancel
+
+
+ Please demonstrate the issue you are writing about.\n\nWhen finished, select the \"Bug?\" button again."
+
+
+ Recording cancelled due to timeoutPlease exit the feedback dialog to access the research log menu
diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java
index f66d55bdd..b985fda21 100644
--- a/java/src/com/android/inputmethod/research/FeedbackActivity.java
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -28,24 +28,9 @@ public class FeedbackActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.research_feedback_activity);
final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout);
- final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history);
- final CharSequence cs = checkbox.getText();
- final String actualString = String.format(cs.toString(),
- ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE);
- checkbox.setText(actualString);
layout.setActivity(this);
}
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
@Override
public void onBackPressed() {
ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
index fee61a923..11a833a85 100644
--- a/java/src/com/android/inputmethod/research/FeedbackFragment.java
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.text.Editable;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -30,10 +31,18 @@ import android.widget.EditText;
import com.android.inputmethod.latin.R;
-public class FeedbackFragment extends Fragment {
+public class FeedbackFragment extends Fragment implements OnClickListener {
+ private static final String TAG = FeedbackFragment.class.getSimpleName();
+
+ private static final String KEY_FEEDBACK_STRING = "FeedbackString";
+ private static final String KEY_INCLUDE_ACCOUNT_NAME = "IncludeAccountName";
+ public static final String KEY_HAS_USER_RECORDING = "HasRecording";
+
private EditText mEditText;
- private CheckBox mIncludingHistoryCheckBox;
private CheckBox mIncludingAccountNameCheckBox;
+ private CheckBox mIncludingUserRecordingCheckBox;
+ private Button mSendButton;
+ private Button mCancelButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -41,39 +50,96 @@ public class FeedbackFragment extends Fragment {
final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
false);
mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
- mIncludingHistoryCheckBox = (CheckBox) view.findViewById(
- R.id.research_feedback_include_history);
+ mEditText.requestFocus();
mIncludingAccountNameCheckBox = (CheckBox) view.findViewById(
R.id.research_feedback_include_account_name);
+ mIncludingUserRecordingCheckBox = (CheckBox) view.findViewById(
+ R.id.research_feedback_include_recording_checkbox);
+ mIncludingUserRecordingCheckBox.setOnClickListener(this);
- final Button sendButton = (Button) view.findViewById(
- R.id.research_feedback_send_button);
- sendButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Editable editable = mEditText.getText();
- final String feedbackContents = editable.toString();
- final boolean isIncludingHistory = mIncludingHistoryCheckBox.isChecked();
- final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked();
- ResearchLogger.getInstance().sendFeedback(feedbackContents, isIncludingHistory,
- isIncludingAccountName);
- final Activity activity = FeedbackFragment.this.getActivity();
- activity.finish();
- ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ mSendButton = (Button) view.findViewById(R.id.research_feedback_send_button);
+ mSendButton.setOnClickListener(this);
+ mCancelButton = (Button) view.findViewById(R.id.research_feedback_cancel_button);
+ mCancelButton.setOnClickListener(this);
+
+ if (savedInstanceState != null) {
+ Log.d(TAG, "restoring from savedInstanceState");
+ restoreState(savedInstanceState);
+ } else {
+ final Bundle bundle = getActivity().getIntent().getExtras();
+ if (bundle != null) {
+ Log.d(TAG, "restoring from getArguments()");
+ restoreState(bundle);
}
- });
-
- final Button cancelButton = (Button) view.findViewById(
- R.id.research_feedback_cancel_button);
- cancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- final Activity activity = FeedbackFragment.this.getActivity();
- activity.finish();
- ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
- }
- });
-
+ }
return view;
}
+
+ @Override
+ public void onClick(final View view) {
+ final ResearchLogger researchLogger = ResearchLogger.getInstance();
+ if (view == mIncludingUserRecordingCheckBox) {
+ if (hasUserRecording()) {
+ // Remove the recording
+ setHasUserRecording(false);
+ } else {
+ final Bundle bundle = new Bundle();
+ onSaveInstanceState(bundle);
+
+ // Let the user make a recording
+ getActivity().finish();
+
+ researchLogger.setFeedbackDialogBundle(bundle);
+ researchLogger.onLeavingSendFeedbackDialog();
+ researchLogger.startRecording();
+ }
+ } else if (view == mSendButton) {
+ final Editable editable = mEditText.getText();
+ final String feedbackContents = editable.toString();
+ final boolean isIncludingAccountName = isIncludingAccountName();
+ researchLogger.sendFeedback(feedbackContents,
+ false /* isIncludingHistory */, isIncludingAccountName, hasUserRecording());
+ getActivity().finish();
+ researchLogger.setFeedbackDialogBundle(null);
+ researchLogger.onLeavingSendFeedbackDialog();
+ } else if (view == mCancelButton) {
+ Log.d(TAG, "Finishing");
+ getActivity().finish();
+ researchLogger.setFeedbackDialogBundle(null);
+ researchLogger.onLeavingSendFeedbackDialog();
+ } else {
+ Log.e(TAG, "Unknown view passed to FeedbackFragment.onClick()");
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle bundle) {
+ final String savedFeedbackString = mEditText.getText().toString();
+
+ bundle.putString(KEY_FEEDBACK_STRING, savedFeedbackString);
+ bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, isIncludingAccountName());
+ bundle.putBoolean(KEY_HAS_USER_RECORDING, hasUserRecording());
+ }
+
+ public void restoreState(final Bundle bundle) {
+ mEditText.setText(bundle.getString(KEY_FEEDBACK_STRING));
+ setIsIncludingAccountName(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME));
+ setHasUserRecording(bundle.getBoolean(KEY_HAS_USER_RECORDING));
+ }
+
+ private boolean hasUserRecording() {
+ return mIncludingUserRecordingCheckBox.isChecked();
+ }
+
+ private void setHasUserRecording(final boolean hasRecording) {
+ mIncludingUserRecordingCheckBox.setChecked(hasRecording);
+ }
+
+ private boolean isIncludingAccountName() {
+ return mIncludingAccountNameCheckBox.isChecked();
+ }
+
+ private void setIsIncludingAccountName(final boolean isIncludingAccountName) {
+ mIncludingAccountNameCheckBox.setChecked(isIncludingAccountName);
+ }
}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 925a72e45..c4d53e10a 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -39,6 +39,8 @@ import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
@@ -70,8 +72,17 @@ import com.android.inputmethod.latin.RichInputConnection.Range;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.MotionEventReader.ReplayData;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -88,8 +99,18 @@ import java.util.UUID;
* This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
*/
public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+ // TODO: This class has grown quite large and combines several concerns that should be
+ // separated. The following refactorings will be applied as soon as possible after adding
+ // support for replaying historical events, fixing some replay bugs, adding some ui constraints
+ // on the feedback dialog, and adding the survey dialog.
+ // TODO: Refactor. Move splash screen code into separate class.
+ // TODO: Refactor. Move feedback screen code into separate class.
+ // TODO: Refactor. Move logging invocations into their own class.
+ // TODO: Refactor. Move currentLogUnit management into separate class.
private static final String TAG = ResearchLogger.class.getSimpleName();
private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
+ private static final boolean DEBUG_REPLAY_AFTER_FEEDBACK = false
+ && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
// Whether the TextView contents are logged at the end of the session. true will disclose
// private info.
private static final boolean LOG_FULL_TEXTVIEW_CONTENTS = false
@@ -153,7 +174,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
/* 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 = 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
@@ -162,12 +183,14 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// used to check whether words are not unique
private Suggest mSuggest;
private MainKeyboardView mMainKeyboardView;
+ // TODO: Check whether a superclass can be used instead of LatinIME.
private LatinIME mLatinIME;
private final Statistics mStatistics;
private final MotionEventReader mMotionEventReader = new MotionEventReader();
private final Replayer mReplayer = new Replayer();
private Intent mUploadIntent;
+ private Intent mUploadNowIntent;
private LogUnit mCurrentLogUnit = new LogUnit();
@@ -176,6 +199,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// thereby leaking private data, we store the time of the down event that started the second
// gesture, and when committing the earlier word, split the LogUnit.
private long mSavedDownEventTime;
+ private Bundle mFeedbackDialogBundle = null;
+ private boolean mInFeedbackDialog = false;
+ // The feedback dialog causes stop() to be called for the keyboard connected to the original
+ // window. This is because the feedback dialog must present its own EditText box that displays
+ // a keyboard. stop() normally causes mFeedbackLogBuffer, which contains the user's data, to be
+ // cleared, and causes mFeedbackLog, which is ready to collect information in case the user
+ // wants to upload, to be closed. This is good because we don't need to log information about
+ // what the user is typing in the feedback dialog, but bad because this data must be uploaded.
+ // Here we save the LogBuffer and Log so the feedback dialog can later access their data.
+ private LogBuffer mSavedFeedbackLogBuffer;
+ private ResearchLog mSavedFeedbackLog;
+ private Handler mUserRecordingTimeoutHandler;
+ private static final long USER_RECORDING_TIMEOUT_MS = 30L * DateUtils.SECOND_IN_MILLIS;
+
private ResearchLogger() {
mStatistics = Statistics.getInstance();
}
@@ -221,6 +258,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mLatinIME = latinIME;
mPrefs = prefs;
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) {
@@ -540,16 +579,41 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
presentFeedbackDialog(latinIME);
}
- private void cancelRecording() {
- if (mUserRecordingLog != null) {
- mUserRecordingLog.abort();
+ public void presentFeedbackDialog(LatinIME latinIME) {
+ if (isMakingUserRecording()) {
+ saveRecording();
}
- mUserRecordingLog = null;
- mUserRecordingLogBuffer = null;
+ mInFeedbackDialog = true;
+ mSavedFeedbackLogBuffer = mFeedbackLogBuffer;
+ mSavedFeedbackLog = mFeedbackLog;
+ // Set the non-saved versions to null so that the stop() caused by switching to the
+ // Feedback dialog will not close them.
+ mFeedbackLogBuffer = null;
+ mFeedbackLog = null;
+
+ Intent intent = new Intent();
+ intent.setClass(mLatinIME, FeedbackActivity.class);
+ if (mFeedbackDialogBundle != null) {
+ Log.d(TAG, "putting extra in feedbackdialogbundle");
+ intent.putExtras(mFeedbackDialogBundle);
+ }
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ latinIME.startActivity(intent);
}
- private void startRecording() {
- // Don't record the "start recording" motion.
+ public void setFeedbackDialogBundle(final Bundle bundle) {
+ mFeedbackDialogBundle = bundle;
+ }
+
+ public void startRecording() {
+ final Resources res = mLatinIME.getResources();
+ Toast.makeText(mLatinIME,
+ res.getString(R.string.research_feedback_demonstration_instructions),
+ Toast.LENGTH_LONG).show();
+ startRecordingInternal();
+ }
+
+ private void startRecordingInternal() {
commitCurrentLogUnit();
if (mUserRecordingLog != null) {
mUserRecordingLog.abort();
@@ -557,6 +621,46 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mUserRecordingFile = createUserRecordingFile(mFilesDir);
mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME);
mUserRecordingLogBuffer = new LogBuffer();
+ resetRecordingTimer();
+ }
+
+ private boolean isMakingUserRecording() {
+ return mUserRecordingLog != null;
+ }
+
+ private void resetRecordingTimer() {
+ if (mUserRecordingTimeoutHandler == null) {
+ mUserRecordingTimeoutHandler = new Handler();
+ }
+ clearRecordingTimer();
+ mUserRecordingTimeoutHandler.postDelayed(mRecordingHandlerTimeoutRunnable,
+ USER_RECORDING_TIMEOUT_MS);
+ }
+
+ private void clearRecordingTimer() {
+ mUserRecordingTimeoutHandler.removeCallbacks(mRecordingHandlerTimeoutRunnable);
+ }
+
+ private Runnable mRecordingHandlerTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelRecording();
+ requestIndicatorRedraw();
+ final Resources res = mLatinIME.getResources();
+ Toast.makeText(mLatinIME, res.getString(R.string.research_feedback_recording_failure),
+ Toast.LENGTH_LONG).show();
+ }
+ };
+
+ private void cancelRecording() {
+ if (mUserRecordingLog != null) {
+ mUserRecordingLog.abort();
+ }
+ mUserRecordingLog = null;
+ mUserRecordingLogBuffer = null;
+ if (mFeedbackDialogBundle != null) {
+ mFeedbackDialogBundle.putBoolean("HasRecording", false);
+ }
}
private void saveRecording() {
@@ -565,29 +669,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mUserRecordingLog.close(null);
mUserRecordingLog = null;
mUserRecordingLogBuffer = null;
- }
- private boolean mInFeedbackDialog = false;
-
- // The feedback dialog causes stop() to be called for the keyboard connected to the original
- // window. This is because the feedback dialog must present its own EditText box that displays
- // a keyboard. stop() normally causes mFeedbackLogBuffer, which contains the user's data, to be
- // cleared, and causes mFeedbackLog, which is ready to collect information in case the user
- // wants to upload, to be closed. This is good because we don't need to log information about
- // what the user is typing in the feedback dialog, but bad because this data must be uploaded.
- // Here we save the LogBuffer and Log so the feedback dialog can later access their data.
- private LogBuffer mSavedFeedbackLogBuffer;
- private ResearchLog mSavedFeedbackLog;
-
- public void presentFeedbackDialog(LatinIME latinIME) {
- mInFeedbackDialog = true;
- mSavedFeedbackLogBuffer = mFeedbackLogBuffer;
- mSavedFeedbackLog = mFeedbackLog;
- // Set the non-saved versions to null so that the stop() caused by switching to the
- // Feedback dialog will not close them.
- mFeedbackLogBuffer = null;
- mFeedbackLog = null;
- latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
+ if (mFeedbackDialogBundle != null) {
+ mFeedbackDialogBundle.putBoolean(FeedbackFragment.KEY_HAS_USER_RECORDING, true);
+ }
+ clearRecordingTimer();
}
// TODO: currently unreachable. Remove after being sure enable/disable is
@@ -650,19 +736,38 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
private static final LogStatement LOGSTATEMENT_FEEDBACK =
- new LogStatement("UserFeedback", false, false, "contents", "accountName");
+ new LogStatement("UserFeedback", false, false, "contents", "accountName", "recording");
public void sendFeedback(final String feedbackContents, final boolean includeHistory,
- final boolean isIncludingAccountName) {
+ final boolean isIncludingAccountName, final boolean isIncludingRecording) {
if (mSavedFeedbackLogBuffer == null) {
return;
}
if (!includeHistory) {
mSavedFeedbackLogBuffer.clear();
}
+ String recording = "";
+ if (isIncludingRecording) {
+ // Try to read recording from recently written json file
+ if (mUserRecordingFile != null) {
+ try {
+ final FileChannel channel =
+ new FileInputStream(mUserRecordingFile).getChannel();
+ final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
+ channel.size());
+ // Android's openFileOutput() creates the file, so we use Android's default
+ // Charset (UTF-8) here to read it.
+ recording = Charset.defaultCharset().decode(buffer).toString();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
final LogUnit feedbackLogUnit = new LogUnit();
final String accountName = isIncludingAccountName ? getAccountName() : "";
feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(),
- feedbackContents, accountName);
+ feedbackContents, accountName, recording);
mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
publishLogBuffer(mFeedbackLogBuffer, mSavedFeedbackLog, true /* isIncludingPrivateData */);
mSavedFeedbackLog.close(new Runnable() {
@@ -671,13 +776,25 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
uploadNow();
}
});
+
+ if (isIncludingRecording && DEBUG_REPLAY_AFTER_FEEDBACK) {
+ final Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ final ReplayData replayData =
+ mMotionEventReader.readMotionEventData(mUserRecordingFile);
+ mReplayer.replay(replayData);
+ }
+ }, 1000);
+ }
}
public void uploadNow() {
if (DEBUG) {
Log.d(TAG, "calling uploadNow()");
}
- mLatinIME.startService(mUploadIntent);
+ mLatinIME.startService(mUploadNowIntent);
}
public void onLeavingSendFeedbackDialog() {
@@ -720,11 +837,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
int height) {
// TODO: Reimplement using a keyboard background image specific to the ResearchLogger
// and remove this method.
- // The check for MainKeyboardView ensures that a red border is only placed around
- // the main keyboard, not every keyboard.
+ // The check for MainKeyboardView ensures that the indicator only decorates the main
+ // keyboard, not every keyboard.
if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
final int savedColor = paint.getColor();
- paint.setColor(Color.RED);
+ paint.setColor(isMakingUserRecording() ? Color.YELLOW : Color.RED);
final Style savedStyle = paint.getStyle();
paint.setStyle(Style.STROKE);
final float savedStrokeWidth = paint.getStrokeWidth();
@@ -733,10 +850,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
canvas.drawLine(0, 0, 0, height, paint);
canvas.drawLine(width, 0, width, height, paint);
} else {
- // Put a tiny red dot on the screen so a knowledgeable user can check whether
- // it is enabled. The dot is actually a zero-width, zero-height rectangle,
- // placed at the lower-right corner of the canvas, painted with a non-zero border
- // width.
+ // Put a tiny dot on the screen so a knowledgeable user can check whether it is
+ // enabled. The dot is actually a zero-width, zero-height rectangle, placed at the
+ // lower-right corner of the canvas, painted with a non-zero border width.
paint.setStrokeWidth(3);
canvas.drawRect(width, height, width, height, paint);
}
@@ -1070,6 +1186,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// LogUnit, not the earlier (the test is for inequality).
researchLogger.setSavedDownEventTime(eventTime - 1);
}
+ // Refresh the timer in case we are capturing user feedback.
+ if (researchLogger.isMakingUserRecording()) {
+ researchLogger.resetRecordingTimer();
+ }
}
}
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
index 69fb36d9c..89c67fbb2 100644
--- a/java/src/com/android/inputmethod/research/UploaderService.java
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -51,7 +51,7 @@ public final class UploaderService extends IntentService {
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;
- private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName()
+ 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;