Merge "ResearchLogger feedback form"

This commit is contained in:
Kurt Partridge 2012-07-22 18:53:30 -07:00 committed by Android (Google) Code Review
commit f0c6606807
9 changed files with 539 additions and 63 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -222,29 +222,47 @@
<!-- Title for input language selection screen --> <!-- Title for input language selection screen -->
<string name="language_selection_title">Input languages</string> <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] --> <!-- 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] --> <!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
<string name="enable_session_logging">Enable logging</string> <!-- TODO: remove translatable=false attribute once text is stable -->
<!-- Title for dialog option to let users log all events in this session [CHAR LIMIT=35] --> <string name="research_enable_session_logging" translatable="false">Enable logging</string>
<string name="log_whole_session_history">Log whole session history</string>
<!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] --> <!-- 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] --> <!-- 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] --> <!-- 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> <!-- TODO: remove translatable=false attribute once text is stable -->
<!-- Toast notification that the system has recorded the whole session history [CHAR LIMIT=35] --> <string name="research_notify_session_log_not_deleted" translatable="false">Session log NOT deleted</string>
<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>
<!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] --> <!-- 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 --> <!-- Preference for input language selection -->
<string name="select_language">Input languages</string> <string name="select_language">Input languages</string>

View file

@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -38,7 +39,6 @@ import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.text.InputType; import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
@ -2111,18 +2111,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}; };
private void launchSettings() { private void launchSettings() {
launchSettingsClass(SettingsActivity.class); handleClose();
launchSubActivity(SettingsActivity.class);
} }
// Called from debug code only // Called from debug code only
public void launchDebugSettings() { public void launchDebugSettings() {
launchSettingsClass(DebugSettingsActivity.class); handleClose();
launchSubActivity(DebugSettingsActivity.class);
} }
private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) { public void launchKeyboardedDialogActivity(Class<? extends Activity> activityClass) {
handleClose(); // 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 intent = new Intent();
intent.setClass(LatinIME.this, settingsClass); intent.setClass(LatinIME.this, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); startActivity(intent);
} }

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -83,7 +83,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final String WHITESPACE_SEPARATORS = " \t\n\r"; 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 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_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(); private static final ResearchLogger sInstance = new ResearchLogger();
// to write to a different filename, e.g., for testing, set mFile before calling start() // to write to a different filename, e.g., for testing, set mFile before calling start()
@ -95,7 +95,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// the system to do so. // the system to do so.
/* package */ ResearchLog mIntentionalResearchLog; /* package */ ResearchLog mIntentionalResearchLog;
// LogUnits are queued here and released only when the user requests the intentional log. // 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 mIsPasswordView = false;
private boolean mIsLoggingSuspended = false; private boolean mIsLoggingSuspended = false;
@ -268,22 +268,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
} }
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() { private void restart() {
stop(); stop();
start(); start();
@ -325,13 +309,17 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
} }
public void presentResearchDialog(final LatinIME latinIME) { 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 CharSequence title = latinIME.getString(R.string.english_ime_research_log);
final boolean showEnable = mIsLoggingSuspended || !sIsLogging; final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
final CharSequence[] items = new CharSequence[] { final CharSequence[] items = new CharSequence[] {
latinIME.getString(R.string.note_timestamp_for_researchlog), latinIME.getString(R.string.research_feedback_menu_option),
showEnable ? latinIME.getString(R.string.enable_session_logging) : showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
latinIME.getString(R.string.do_not_log_this_session), latinIME.getString(R.string.research_do_not_log_this_session)
latinIME.getString(R.string.log_whole_session_history)
}; };
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override @Override
@ -339,9 +327,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
di.dismiss(); di.dismiss();
switch (position) { switch (position) {
case 0: case 0:
userTimestamp(); presentFeedbackDialog(latinIME);
Toast.makeText(latinIME, R.string.notify_recorded_timestamp,
Toast.LENGTH_LONG).show();
break; break;
case 1: case 1:
if (showEnable) { if (showEnable) {
@ -349,11 +335,13 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
setLoggingAllowed(true); setLoggingAllowed(true);
} }
resumeLogging(); resumeLogging();
Toast.makeText(latinIME, R.string.notify_session_logging_enabled, Toast.makeText(latinIME,
R.string.research_notify_session_logging_enabled,
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
} else { } else {
Toast toast = Toast.makeText(latinIME, 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(); toast.show();
boolean isLogDeleted = abort(); boolean isLogDeleted = abort();
final long currentTime = System.currentTimeMillis(); final long currentTime = System.currentTimeMillis();
@ -361,21 +349,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
SUSPEND_DURATION_IN_MINUTES; SUSPEND_DURATION_IN_MINUTES;
suspendLoggingUntil(resumeTime); suspendLoggingUntil(resumeTime);
toast.cancel(); toast.cancel();
Toast.makeText(latinIME, R.string.notify_logging_suspended, Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
} }
break; 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 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
latinIME.showOptionDialog(builder.create()); 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) { public void initSuggest(Suggest suggest) {
mSuggest = suggest; mSuggest = suggest;
} }
@ -540,7 +594,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return; return;
} }
if (mMainResearchLog == null) { if (mMainResearchLog == null) {
Log.w(TAG, "ResearchLog was not properly set up");
return; return;
} }
if (isPrivacySensitive) { if (isPrivacySensitive) {
@ -647,6 +700,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
final SharedPreferences prefs) { final SharedPreferences prefs) {
final ResearchLogger researchLogger = getInstance(); final ResearchLogger researchLogger = getInstance();
if (researchLogger.mInFeedbackDialog) {
researchLogger.saveLogsForFeedback();
}
researchLogger.start(); researchLogger.start();
if (editorInfo != null) { if (editorInfo != null) {
final Context context = researchLogger.mContext; final Context context = researchLogger.mContext;
@ -688,6 +744,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
researchLogger.onWordComplete(scrubbedWord); 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 // Regular logging methods
private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = { private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_PROCESSMOTIONEVENT = {