ResearchLogger feedback form

- also cleaned up RLog menu

multi-project commit with If0fd4fef89d390073e6939d5188ed5696866cb33

Bug: 6188932
Change-Id: I4f66f13bd366b4e8bde742ccd0704f812c6d33f9
diff --git a/java/res/layout/research_feedback_activity.xml b/java/res/layout/research_feedback_activity.xml
new file mode 100644
index 0000000..a6b8b8a
--- /dev/null
+++ b/java/res/layout/research_feedback_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.inputmethod.research.FeedbackLayout
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:layout_width="match_parent"
+     android:layout_height="wrap_content"
+     android:orientation="vertical"
+     android:id="@+id/research_feedback_layout"
+>
+
+    <fragment
+          android:id="@+id/research_feedback_fragment"
+          android:name="com.android.inputmethod.research.FeedbackFragment"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+    />
+</com.android.inputmethod.research.FeedbackLayout>
diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
new file mode 100644
index 0000000..cc04ced
--- /dev/null
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:layout_width="fill_parent"
+     android:layout_height="fill_parent"
+     android:orientation="vertical"
+>
+
+    <!-- Mimic a dialog title.  Necessary since the dialog is actually an activity, so the normal
+        dialog title construction code is not available. -->
+    <LinearLayout
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:orientation="vertical"
+    >
+        <com.android.internal.widget.DialogTitle
+            style="?android:attr/windowTitleStyle"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:layout_width="match_parent"
+            android:layout_height="64dip"
+            android:layout_marginLeft="16dip"
+            android:layout_marginRight="16dip"
+            android:gravity="center_vertical|left"
+            android:text="@string/research_feedback_dialog_title" />
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="2dip"
+            android:background="@android:color/holo_blue_light" />
+    </LinearLayout>
+
+    <EditText
+        android:id="@+id/research_feedback_contents"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_gravity="fill_horizontal|center_vertical"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip"
+        android:layout_marginBottom="8dip"
+        android:layout_marginTop="8dip"
+        android:lines="2"
+        android:hint="@string/research_feedback_hint"
+        android:inputType="textMultiLine"
+        android:imeOptions="flagNoFullscreen"
+    >
+        <requestFocus />
+    </EditText>
+
+    <CheckBox
+        android:id="@+id/research_feedback_include_history"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_marginBottom="8dip"
+        android:checked="true"
+        android:text="@string/research_feedback_include_history_label"
+    />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:divider="?android:attr/dividerHorizontal"
+        android:showDividers="beginning"
+        android:dividerPadding="0dip"
+    >
+        <LinearLayout
+            style="?android:attr/buttonBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:measureWithLargestChild="true"
+        >
+            <Button
+                android:id="@+id/research_feedback_cancel_button"
+                android:layout_width="0dip"
+                android:layout_gravity="left"
+                android:layout_weight="1"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle"
+                android:textSize="14sp"
+                android:text="@string/research_feedback_cancel"
+                android:layout_height="wrap_content"
+            />
+            <Button
+                android:id="@+id/research_feedback_send_button"
+                android:layout_width="0dip"
+                android:layout_gravity="right"
+                android:layout_weight="1"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle"
+                android:textSize="14sp"
+                android:text="@string/research_feedback_send"
+                android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
diff --git a/java/res/layout/research_feedback_layout.xml b/java/res/layout/research_feedback_layout.xml
new file mode 100644
index 0000000..bacd191
--- /dev/null
+++ b/java/res/layout/research_feedback_layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:layout_width="fill_parent"
+     android:layout_height="fill_parent"
+     android:orientation="vertical"
+>
+
+    <EditText
+        android:id="@+id/research_feedback_contents"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_gravity="fill_horizontal|center_vertical"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip"
+        android:layout_marginBottom="8dip"
+        android:layout_marginTop="8dip"
+        android:lines="2"
+        android:hint="@string/research_feedback_hint"
+        android:inputType="textMultiLine"
+        android:imeOptions="flagNoFullscreen"
+        android:focusable="true"
+    >
+        <requestFocus />
+    </EditText>
+
+    <CheckBox
+        android:id="@+id/research_feedback_include_history"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_marginBottom="8dip"
+        android:checked="true"
+        android:text="@string/research_feedback_include_history_label"
+    />
+</LinearLayout>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 98c8918..377018a 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -222,29 +222,47 @@
     <!-- Title for input language selection screen -->
     <string name="language_selection_title">Input languages</string>
 
-    <!-- Title for dialog option that lets user mark a particular time in the log for later review by experts [CHAR LIMIT=38] -->
-    <string name="note_timestamp_for_researchlog">Note timestamp in log</string>
-    <!-- Toast notification message that the time has been marked for later review. [CHAR LIMIT=25] -->
-    <string name="notify_recorded_timestamp">Recorded timestamp</string>
-
     <!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] -->
-    <string name="do_not_log_this_session">Suspend logging</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_do_not_log_this_session" translatable="false">Suspend logging</string>
     <!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
-    <string name="enable_session_logging">Enable logging</string>
-    <!--  Title for dialog option to let users log all events in this session [CHAR LIMIT=35] -->
-    <string name="log_whole_session_history">Log whole session history</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_enable_session_logging" translatable="false">Enable logging</string>
     <!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] -->
-    <string name="notify_session_log_deleting">Deleting session log</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_notify_session_log_deleting" translatable="false">Deleting session log</string>
     <!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] -->
-    <string name="notify_logging_suspended">Logging temporarily suspended.  To disable permanently, go to Android Keyboard Settings</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_notify_logging_suspended" translatable="false">Logging temporarily suspended.  To disable permanently, go to Android Keyboard Settings</string>
     <!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] -->
-    <string name="notify_session_log_not_deleted">Session log NOT deleted</string>
-    <!-- Toast notification that the system has recorded the whole session history [CHAR LIMIT=35] -->
-    <string name="notify_session_history_logged">Session history logged</string>
-    <!-- Toast notification that the system has failed to record the whole session history [CHAR LIMIT=35] -->
-    <string name="notify_session_history_not_logged">Error: Session history NOT logged</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_notify_session_log_not_deleted" translatable="false">Session log NOT deleted</string>
     <!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] -->
-    <string name="notify_session_logging_enabled">Session logging enabled</string>
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_notify_session_logging_enabled" translatable="false">Session logging enabled</string>
+
+    <!-- Menu option that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_menu_option" translatable="false">Send feedback</string>
+    <!-- Dialog box title that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_dialog_title" translatable="false">Send feedback</string>
+    <!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_include_history_label" translatable="false">Include last 5 words entered</string>
+    <!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_hint" translatable="false">Enter your feedback here.</string>
+    <!-- Dialog button choice to send research feedback [CHAR LIMIT=35] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_send" translatable="false">Send</string>
+    <!-- Dialog button choice to cancel sending research feedback [CHAR LIMIT=35] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_feedback_cancel" translatable="false">Cancel</string>
+    <!-- Toast notification to ask user to quit the research feedback dialog to perform this operation [CHAR LIMIT=100] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_please_exit_feedback_form" translatable="false">Please exit the feedback dialog to access the research log menu</string>
+
     <!-- Preference for input language selection -->
     <string name="select_language">Input languages</string>
 
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8953cb8..4248921 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -20,6 +20,7 @@
 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
 import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -38,7 +39,6 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemClock;
-import android.preference.PreferenceActivity;
 import android.preference.PreferenceManager;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -2111,18 +2111,26 @@
     };
 
     private void launchSettings() {
-        launchSettingsClass(SettingsActivity.class);
+        handleClose();
+        launchSubActivity(SettingsActivity.class);
     }
 
     // Called from debug code only
     public void launchDebugSettings() {
-        launchSettingsClass(DebugSettingsActivity.class);
+        handleClose();
+        launchSubActivity(DebugSettingsActivity.class);
     }
 
-    private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
-        handleClose();
+    public void launchKeyboardedDialogActivity(Class<? extends Activity> activityClass) {
+        // Put the text in the attached EditText into a safe, saved state before switching to a
+        // new activity that will also use the soft keyboard.
+        commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+        launchSubActivity(activityClass);
+    }
+
+    private void launchSubActivity(Class<? extends Activity> activityClass) {
         Intent intent = new Intent();
-        intent.setClass(LatinIME.this, settingsClass);
+        intent.setClass(LatinIME.this, activityClass);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent);
     }
diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java
new file mode 100644
index 0000000..c9f3b47
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackActivity extends Activity {
+    @Override
+    protected void onCreate(final Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.research_feedback_activity);
+        final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout);
+        layout.setActivity(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    public void onBackPressed() {
+        ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+        super.onBackPressed();
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
new file mode 100644
index 0000000..a2e08e2
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackFragment extends Fragment {
+    private EditText mEditText;
+    private CheckBox mCheckBox;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
+                false);
+        mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
+        mCheckBox = (CheckBox) view.findViewById(R.id.research_feedback_include_history);
+
+        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 includeHistory = mCheckBox.isChecked();
+                ResearchLogger.getInstance().sendFeedback(feedbackContents, includeHistory);
+                final Activity activity = FeedbackFragment.this.getActivity();
+                activity.finish();
+                ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+            }
+        });
+
+        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;
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackLayout.java b/java/src/com/android/inputmethod/research/FeedbackLayout.java
new file mode 100644
index 0000000..f2cbfe3
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackLayout.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+public class FeedbackLayout extends LinearLayout {
+    private Activity mActivity;
+
+    public FeedbackLayout(Context context) {
+        super(context);
+    }
+
+    public FeedbackLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FeedbackLayout(Context context, AttributeSet attrs, int defstyle) {
+        super(context, attrs, defstyle);
+    }
+
+    public void setActivity(Activity activity) {
+        mActivity = activity;
+    }
+
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            KeyEvent.DispatcherState state = getKeyDispatcherState();
+            if (state != null) {
+                if (event.getAction() == KeyEvent.ACTION_DOWN
+                        && event.getRepeatCount() == 0) {
+                    state.startTracking(event, this);
+                    return true;
+                } else if (event.getAction() == KeyEvent.ACTION_UP
+                        && !event.isCanceled() && state.isTracking(event)) {
+                    mActivity.onBackPressed();
+                    return true;
+                }
+            }
+        }
+        return super.dispatchKeyEventPreIme(event);
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index b40bfe3..e6828da 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -83,7 +83,7 @@
     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 int ABORT_TIMEOUT_IN_MS = 10 * 1000;
+    private static final int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user
 
     private static final ResearchLogger sInstance = new ResearchLogger();
     // to write to a different filename, e.g., for testing, set mFile before calling start()
@@ -95,7 +95,7 @@
     // the system to do so.
     /* package */ ResearchLog mIntentionalResearchLog;
     // LogUnits are queued here and released only when the user requests the intentional log.
-    private final List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+    private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
 
     private boolean mIsPasswordView = false;
     private boolean mIsLoggingSuspended = false;
@@ -268,22 +268,6 @@
         }
     }
 
-    private void logWholeSessionHistory() throws IOException {
-        try {
-            LogUnit headerLogUnit = new LogUnit();
-            headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
-            mIntentionalResearchLog.publishAllEvents(headerLogUnit);
-            for (LogUnit logUnit : mIntentionalResearchLogQueue) {
-                mIntentionalResearchLog.publishAllEvents(logUnit);
-            }
-            mIntentionalResearchLog.stop();
-            mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
-            mIntentionalResearchLog.start();
-        } finally {
-            mIntentionalResearchLogQueue.clear();
-        }
-    }
-
     private void restart() {
         stop();
         start();
@@ -325,13 +309,17 @@
     }
 
     public void presentResearchDialog(final LatinIME latinIME) {
+        if (mInFeedbackDialog) {
+            Toast.makeText(latinIME, R.string.research_please_exit_feedback_form,
+                    Toast.LENGTH_LONG).show();
+            return;
+        }
         final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
         final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
         final CharSequence[] items = new CharSequence[] {
-                latinIME.getString(R.string.note_timestamp_for_researchlog),
-                showEnable ? latinIME.getString(R.string.enable_session_logging) :
-                        latinIME.getString(R.string.do_not_log_this_session),
-                latinIME.getString(R.string.log_whole_session_history)
+                latinIME.getString(R.string.research_feedback_menu_option),
+                showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
+                        latinIME.getString(R.string.research_do_not_log_this_session)
         };
         final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
             @Override
@@ -339,9 +327,7 @@
                 di.dismiss();
                 switch (position) {
                     case 0:
-                        userTimestamp();
-                        Toast.makeText(latinIME, R.string.notify_recorded_timestamp,
-                                Toast.LENGTH_LONG).show();
+                        presentFeedbackDialog(latinIME);
                         break;
                     case 1:
                         if (showEnable) {
@@ -349,11 +335,13 @@
                                 setLoggingAllowed(true);
                             }
                             resumeLogging();
-                            Toast.makeText(latinIME, R.string.notify_session_logging_enabled,
+                            Toast.makeText(latinIME,
+                                    R.string.research_notify_session_logging_enabled,
                                     Toast.LENGTH_LONG).show();
                         } else {
                             Toast toast = Toast.makeText(latinIME,
-                                    R.string.notify_session_log_deleting, Toast.LENGTH_LONG);
+                                    R.string.research_notify_session_log_deleting,
+                                    Toast.LENGTH_LONG);
                             toast.show();
                             boolean isLogDeleted = abort();
                             final long currentTime = System.currentTimeMillis();
@@ -361,21 +349,10 @@
                                     SUSPEND_DURATION_IN_MINUTES;
                             suspendLoggingUntil(resumeTime);
                             toast.cancel();
-                            Toast.makeText(latinIME, R.string.notify_logging_suspended,
+                            Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
                                     Toast.LENGTH_LONG).show();
                         }
                         break;
-                    case 2:
-                        try {
-                            logWholeSessionHistory();
-                            Toast.makeText(latinIME, R.string.notify_session_history_logged,
-                                    Toast.LENGTH_LONG).show();
-                        } catch (IOException e) {
-                            Toast.makeText(latinIME, R.string.notify_session_history_not_logged,
-                                    Toast.LENGTH_LONG).show();
-                            e.printStackTrace();
-                        }
-                        break;
                 }
             }
 
@@ -386,6 +363,83 @@
         latinIME.showOptionDialog(builder.create());
     }
 
+    private boolean mInFeedbackDialog = false;
+    public void presentFeedbackDialog(LatinIME latinIME) {
+        mInFeedbackDialog = true;
+        latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
+    }
+
+    private ResearchLog mFeedbackLog;
+    private List<LogUnit> mFeedbackQueue;
+    private ResearchLog mSavedMainResearchLog;
+    private ResearchLog mSavedIntentionalResearchLog;
+    private List<LogUnit> mSavedIntentionalResearchLogQueue;
+
+    private void saveLogsForFeedback() {
+        mFeedbackLog = mIntentionalResearchLog;
+        if (mIntentionalResearchLogQueue != null) {
+            mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue);
+        } else {
+            mFeedbackQueue = null;
+        }
+        mSavedMainResearchLog = mMainResearchLog;
+        mSavedIntentionalResearchLog = mIntentionalResearchLog;
+        mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue;
+
+        mMainResearchLog = null;
+        mIntentionalResearchLog = null;
+        mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+    }
+
+    private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5;
+    public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+        if (includeHistory && mFeedbackLog != null) {
+            try {
+                LogUnit headerLogUnit = new LogUnit();
+                headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
+                mFeedbackLog.publishAllEvents(headerLogUnit);
+                for (LogUnit logUnit : mFeedbackQueue) {
+                    mFeedbackLog.publishAllEvents(logUnit);
+                }
+                userFeedback(mFeedbackLog, feedbackContents);
+                mFeedbackLog.stop();
+                try {
+                    mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
+                mIntentionalResearchLog.start();
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                mIntentionalResearchLogQueue.clear();
+            }
+            mResearchLogUploader.uploadNow(null);
+        } else {
+            // create a separate ResearchLog just for feedback
+            final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir));
+            try {
+                feedbackLog.start();
+                userFeedback(feedbackLog, feedbackContents);
+                feedbackLog.stop();
+                feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+                mResearchLogUploader.uploadNow(null);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void onLeavingSendFeedbackDialog() {
+        mInFeedbackDialog = false;
+        mMainResearchLog = mSavedMainResearchLog;
+        mIntentionalResearchLog = mSavedIntentionalResearchLog;
+        mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue;
+    }
+
     public void initSuggest(Suggest suggest) {
         mSuggest = suggest;
     }
@@ -540,7 +594,6 @@
             return;
         }
         if (mMainResearchLog == null) {
-            Log.w(TAG, "ResearchLog was not properly set up");
             return;
         }
         if (isPrivacySensitive) {
@@ -647,6 +700,9 @@
     public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
             final SharedPreferences prefs) {
         final ResearchLogger researchLogger = getInstance();
+        if (researchLogger.mInFeedbackDialog) {
+            researchLogger.saveLogsForFeedback();
+        }
         researchLogger.start();
         if (editorInfo != null) {
             final Context context = researchLogger.mContext;
@@ -688,6 +744,20 @@
         researchLogger.onWordComplete(scrubbedWord);
     }
 
+    private static final String[] EVENTKEYS_USER_FEEDBACK = {
+        "UserFeedback", "FeedbackContents"
+    };
+
+    private void userFeedback(ResearchLog researchLog, String feedbackContents) {
+        // this method is special; it directs the feedbackContents to a particular researchLog
+        final LogUnit logUnit = new LogUnit();
+        final Object[] values = {
+            feedbackContents
+        };
+        logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false);
+        researchLog.publishAllEvents(logUnit);
+    }
+
     // Regular logging methods
 
     private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {