Merge branch 'voice' into 101111-remerge-voice

pull/14/head
Tim Su 14 years ago
commit 87ed3e79ff

@ -22,11 +22,12 @@ import com.todoroo.andlib.service.ExceptionService;
import com.todoroo.andlib.service.NotificationManager;
import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.voice.VoiceOutputAssistant;
public class Notifications extends BroadcastReceiver {
@ -94,6 +95,8 @@ public class Notifications extends BroadcastReceiver {
if(!showTaskNotification(id, type, reminder)) {
notificationManager.cancel((int)id);
}
// shutdown the VoiceOutputAssistant for now
VoiceOutputAssistant.getInstance().onDestroy();
}
// --- notification creation
@ -217,6 +220,7 @@ public class Notifications extends BroadcastReceiver {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
int callState = tm.getCallState();
boolean voiceReminder = Preferences.getBoolean(R.string.p_voiceRemindersEnabled, false);
// if non-stop mode is activated, set up the flags for insistent
// notification, and increase the volume to full volume, so the user
// will actually pay attention to the alarm
@ -225,6 +229,7 @@ public class Notifications extends BroadcastReceiver {
notification.audioStreamType = AudioManager.STREAM_ALARM;
audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), 0);
voiceReminder = false;
} else {
notification.audioStreamType = AudioManager.STREAM_NOTIFICATION;
}
@ -232,10 +237,12 @@ public class Notifications extends BroadcastReceiver {
// quiet hours = no sound
if(quietHours || callState != TelephonyManager.CALL_STATE_IDLE) {
notification.sound = null;
voiceReminder = false;
} else {
String notificationPreference = Preferences.getStringValue(R.string.p_rmd_ringtone);
if(audioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
notification.sound = null;
voiceReminder = false;
} else if(notificationPreference != null) {
if(notificationPreference.length() > 0) {
Uri notificationSound = Uri.parse(notificationPreference);
@ -266,6 +273,17 @@ public class Notifications extends BroadcastReceiver {
Log.w("Astrid", "Logging notification: " + text); //$NON-NLS-1$ //$NON-NLS-2$
notificationManager.notify(notificationId, notification);
if (voiceReminder) {
while (audioManager.getMode() == AudioManager.MODE_RINGTONE) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
VoiceOutputAssistant.getInstance().queueSpeak(text);
}
}
/**

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

@ -88,18 +88,31 @@
android:layout_height="wrap_content"
android:text="@string/TEA_note_label"
style="@style/TextAppearance.GEN_EditLabel" />
<EditText
android:id="@+id/notes"
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:scrollbars="vertical"
android:gravity="top"
android:hint="@string/TEA_notes_hint"
android:autoText="true"
android:capitalize="sentences"
android:singleLine="false" />
android:layout_height="wrap_content">
<!-- Voice Add Note Button -->
<ImageButton android:id="@+id/voiceAddNoteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/ic_btn_speak_now"
android:scaleType="fitCenter"
android:visibility="gone"/>
<EditText
android:id="@+id/notes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="100"
android:scrollbars="vertical"
android:gravity="top"
android:hint="@string/TEA_notes_hint"
android:autoText="true"
android:capitalize="sentences"
android:singleLine="false" />
</LinearLayout>
<!-- buttons -->
<LinearLayout
android:orientation="horizontal"

@ -48,6 +48,15 @@
android:layout_weight="1"
android:orientation="horizontal">
<!-- Voice Add Button -->
<ImageButton android:id="@+id/voiceAddButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
android:src="@drawable/ic_btn_speak_now"
android:scaleType="fitCenter"/>
<!-- Quick Add Button -->
<ImageButton android:id="@+id/quickAddButton"
android:layout_width="wrap_content"

@ -435,11 +435,20 @@
<!-- Preference: Task List Show Notes -->
<string name="EPr_showNotes_title">Notizen zu Aufgaben anzeigen</string>
<!-- Preference: Task List Show Voice-button if recognition-service is available -->
<string name="EPr_voiceInputEnabled_title">Spracheingabe aktivieren</string>
<!-- Preference: Task List Show Notes Description (disabled) -->
<string name="EPr_showNotes_desc_disabled">Notizen werden angezeigt, wenn Sie auf eine Aufgabe tippen</string>
<!-- Preference: Task List Show Notes Description (enabled) -->
<string name="EPr_showNotes_desc_enabled">Notizen werden immer angezeigt</string>
<!-- Preference Category: Voice Title -->
<string name="EPr_voice_header">Sprachfunktionen</string>
<!-- Preference: Task List Voice Input Description (disabled) -->
<string name="EPr_voiceInput_desc_disabled">Mikrofon-Button wird ausgeblendet</string>
<!-- Preference: Task List Voice Input Description (enabled) -->
<string name="EPr_voiceInput_desc_enabled">Mikrofon-Button wird angezeigt</string>
<!-- Preference Category: Defaults Title -->
<string name="EPr_defaults_header">Neue Standardeinstellungen für Aufgaben</string>

@ -22,6 +22,9 @@
<!-- ringtone to use for notifications -->
<string name="p_rmd_ringtone">notification_ringtone</string>
<!-- if tts is used for notifications -->
<string name="p_rmd_voice">notification_voice</string>
<!-- icon theme for notifications -->
<string name="p_rmd_icon">notif_theme</string>
@ -127,6 +130,9 @@
<string name="p_colorize">colorize</string>
<string name="p_fontSize">font_size</string>
<string name="p_showNotes">notesVisible</string>
<string name="p_voiceInputEnabled">voiceInputEnabled</string>
<string name="p_voiceInputCreatesTask">voiceInputCreatesTask</string>
<string name="p_voiceRemindersEnabled">voiceRemindersEnabled</string>
<string-array name="EPr_font_size">
<!-- font_size: options for preference menu -->

@ -90,6 +90,10 @@
<!-- Quick Add Edit Box Hint-->
<string name="TLA_quick_add_hint">Add to this list...</string>
<!-- Voice Add Prompt Text -->
<string name="TLA_voice_add_prompt">Speak to add a task</string>
<string name="TLA_voice_edit_prompt">Speak to add to tasktitle</string>
<!-- ====================================================== TaskAdapter == -->
<!-- Format string to indicate task is hidden (%s => task name) -->
@ -209,6 +213,12 @@
<!-- Third Tab - add-ons -->
<string name="TEA_tab_addons">Add-ons</string>
<!-- Prompt for tasktitle voiceinput -->
<string name="TEA_voice_edit_title_prompt">Speak to add to current title</string>
<!-- Prompt for tasknote voiceinput -->
<string name="TEA_voice_edit_note_prompt">Speak to add to current note</string>
<!-- Task title label -->
<string name="TEA_title_label">Title</string>
@ -330,11 +340,30 @@
<!-- Preference: Task List Show Notes -->
<string name="EPr_showNotes_title">Show Notes In Task</string>
<!-- Preference: Task List recognition-service is not installed, but available -->
<string name="EPr_voiceInputInstall_dlg">Voice-input is not installed.\nDo you want to go to the market and install it?</string>
<!-- Preference: Task List recognition-service is not available for this system -->
<string name="EPr_voiceInputUnavailable_dlg">Unfortunately voice-input is not available for your system.\nIf possible, please update your system to 2.1 or later.</string>
<!-- Preference: Market is not available for this system -->
<string name="EPr_marketUnavailable_dlg">Unfortunately the market is not available for your system.\nIf possible, try downloading voicesearch from another source.</string>
<!-- Preference: Task List Show Voice-button if recognition-service is available -->
<string name="EPr_voiceInputEnabled_title">Enable voice-input</string>
<!-- Preference: Task List Voice-button directly creates tasks -->
<string name="EPr_voiceInputCreatesTask_title">Enable direct task-creation per voice</string>
<!-- Preference: Voice reminders if TTS-service is available -->
<string name="EPr_voiceRemindersEnabled_title">Enable voice-reminders</string>
<!-- Preference: Task List Show Notes Description (disabled) -->
<string name="EPr_showNotes_desc_disabled">Notes will be displayed when you tap a task</string>
<!-- Preference: Task List Show Notes Description (enabled) -->
<string name="EPr_showNotes_desc_enabled">Notes will always be displayed</string>
<!-- Preference Category: Voice Title -->
<string name="EPr_voice_header">Voice</string>
<!-- Preference: Task List Voice Input Description (disabled) -->
<string name="EPr_voiceInput_desc_disabled">Voice-Button will be hidden</string>
<!-- Preference: Task List Voice Input Description (enabled) -->
<string name="EPr_voiceInput_desc_enabled">Voice-Button will be shown</string>
<!-- Preference Category: Defaults Title -->
<string name="EPr_defaults_header">New Task Defaults</string>

@ -26,4 +26,19 @@
android:defaultValue="true" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/EPr_voice_header">
<CheckBoxPreference
android:key="@string/p_voiceInputEnabled"
android:title="@string/EPr_voiceInputEnabled_title"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/p_voiceInputCreatesTask"
android:title="@string/EPr_voiceInputCreatesTask_title"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/p_voiceRemindersEnabled"
android:title="@string/EPr_voiceRemindersEnabled_title"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>

@ -11,15 +11,20 @@ import java.util.Map.Entry;
import org.weloveastrid.rmilk.MilkPreferences;
import org.weloveastrid.rmilk.MilkUtilities;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
import android.widget.Toast;
import com.timsu.astrid.R;
@ -27,6 +32,7 @@ import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.andlib.widget.TodorooPreferences;
@ -38,6 +44,8 @@ import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Flags;
import com.todoroo.astrid.voice.VoiceInputAssistant;
import com.todoroo.astrid.voice.VoiceOutputAssistant;
/**
* Displays the preference screen for users to edit their preferences
@ -57,6 +65,8 @@ public class EditPreferences extends TodorooPreferences {
@Autowired
private Database database;
private VoiceInputAssistant voiceInputAssistant;
public EditPreferences() {
DependencyInjectionService.getInstance().inject(this);
}
@ -73,6 +83,7 @@ public class EditPreferences extends TodorooPreferences {
ContextManager.setContext(this);
PreferenceScreen screen = getPreferenceScreen();
voiceInputAssistant = new VoiceInputAssistant(this);
// load plug-ins
Intent queryIntent = new Intent(AstridApiConstants.ACTION_SETTINGS);
@ -189,8 +200,8 @@ public class EditPreferences extends TodorooPreferences {
}
@Override
public void updatePreferences(Preference preference, Object value) {
Resources r = getResources();
public void updatePreferences(final Preference preference, Object value) {
final Resources r = getResources();
if (r.getString(R.string.p_showNotes).equals(preference.getKey())) {
if (value != null && !(Boolean)value)
preference.setSummary(R.string.EPr_showNotes_desc_disabled);
@ -200,6 +211,64 @@ public class EditPreferences extends TodorooPreferences {
taskService.clearDetails(Criterion.all);
Flags.set(Flags.REFRESH);
}
} else if (r.getString(R.string.p_voiceInputEnabled).equals(preference.getKey())) {
if (value != null && (Boolean)value)
if (!voiceInputAssistant.isVoiceInputAvailable()) {
// voicesearch available since 2.1
if (AndroidUtilities.getSdkVersion() > 6) {
DialogUtilities.okCancelDialog(this,
r.getString(R.string.EPr_voiceInputInstall_dlg),
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();
// User wants to install voicesearch, take him to the market
Intent marketIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://search?q=pname:" + //$NON-NLS-1$
"com.google.android.voicesearch.x"));
try {
startActivity(marketIntent);
} catch (ActivityNotFoundException ane) {
DialogUtilities.okDialog(EditPreferences.this,
r.getString(R.string.EPr_marketUnavailable_dlg),
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((CheckBoxPreference)preference).setChecked(false);
dialog.dismiss();
}
});
}
}
},
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((CheckBoxPreference)preference).setChecked(false);
dialog.dismiss();
}
});
} else {
DialogUtilities.okDialog(this,
r.getString(R.string.EPr_voiceInputUnavailable_dlg),
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((CheckBoxPreference)preference).setChecked(false);
dialog.dismiss();
}
});
}
}
} else if (r.getString(R.string.p_voiceRemindersEnabled).equals(preference.getKey())) {
if (value != null && (Boolean)value)
VoiceOutputAssistant.getInstance().checkIsTTSInstalled();
}
else if (r.getString(R.string.p_statistics).equals(preference.getKey())) {
if (value != null && !(Boolean)value)
@ -209,5 +278,11 @@ public class EditPreferences extends TodorooPreferences {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
VoiceOutputAssistant.getInstance().handleActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
}

@ -40,6 +40,7 @@ import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
@ -90,6 +91,7 @@ import com.todoroo.astrid.tags.TagsControlSet;
import com.todoroo.astrid.timers.TimerControlSet;
import com.todoroo.astrid.ui.DeadlineTimePickerDialog;
import com.todoroo.astrid.ui.DeadlineTimePickerDialog.OnDeadlineTimeSetListener;
import com.todoroo.astrid.voice.VoiceInputAssistant;
/**
* This activity is responsible for creating new tasks and editing existing
@ -154,6 +156,9 @@ public final class TaskEditActivity extends TabActivity {
// --- UI components
private ImageButton voiceAddNoteButton;
private EditTextControlSet notesControlSet = null;
private EditText title;
private final List<TaskEditControlSet> controls =
@ -173,6 +178,11 @@ public final class TaskEditActivity extends TabActivity {
/** edit control receiver */
private final ControlReceiver controlReceiver = new ControlReceiver();
/** voice assistant for notes-creation */
private VoiceInputAssistant voiceNoteAssistant = null;
private EditText notesEditText;
/* ======================================================================
* ======================================================= initialization
* ====================================================================== */
@ -235,6 +245,15 @@ public final class TaskEditActivity extends TabActivity {
controls.add(new ImportanceControlSet(R.id.importance_container));
controls.add(new UrgencyControlSet(R.id.urgency));
// prepare and set listener for voice-button
voiceAddNoteButton = (ImageButton) findViewById(R.id.voiceAddNoteButton);
notesEditText = (EditText) findViewById(R.id.notes);
int prompt = R.string.TEA_voice_edit_note_prompt;
voiceNoteAssistant = new VoiceInputAssistant(this, voiceAddNoteButton,
notesEditText);
voiceNoteAssistant.setLanguageModel(RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
voiceNoteAssistant.configureMicrophoneButton(prompt);
new Thread() {
@Override
public void run() {
@ -254,7 +273,7 @@ public final class TaskEditActivity extends TabActivity {
try {
if(ProducteevUtilities.INSTANCE.isLoggedIn()) {
controls.add(new ProducteevControlSet(TaskEditActivity.this, addonsAddons));
((TextView)findViewById(R.id.notes)).setHint(R.string.producteev_TEA_notes);
notesEditText.setHint(R.string.producteev_TEA_notes);
((TextView)findViewById(R.id.notes_label)).setHint(R.string.producteev_TEA_notes);
}
} catch (Exception e) {
@ -286,7 +305,8 @@ public final class TaskEditActivity extends TabActivity {
}
});
controls.add(new EditTextControlSet(Task.NOTES, R.id.notes));
notesControlSet = new EditTextControlSet(Task.NOTES, R.id.notes);
controls.add(notesControlSet);
controls.add( new ReminderControlSet(R.id.reminder_due,
R.id.reminder_overdue, R.id.reminder_alarm));
controls.add( new RandomReminderControlSet(R.id.reminder_random,
@ -596,6 +616,18 @@ public final class TaskEditActivity extends TabActivity {
populateFields();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// handle the result of voice recognition, put it into the appropiate textfield
voiceNoteAssistant.handleActivityResult(requestCode, resultCode, data);
// write the voicenote into the model, or it will be deleted by onResume.populateFields
// (due to the activity-change)
notesControlSet.writeToModel(model);
super.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@ -604,6 +636,11 @@ public final class TaskEditActivity extends TabActivity {
outState.putParcelable(TASK_IN_PROGRESS, model);
}
@Override
protected void onRestoreInstanceState(Bundle inState) {
super.onRestoreInstanceState(inState);
}
@Override
protected void onStart() {
super.onStart();

@ -91,6 +91,7 @@ import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.utility.AstridPreferences;
import com.todoroo.astrid.utility.Constants;
import com.todoroo.astrid.utility.Flags;
import com.todoroo.astrid.voice.VoiceInputAssistant;
import com.todoroo.astrid.widget.TasksWidget;
/**
@ -157,6 +158,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
protected int sortFlags;
protected int sortSort;
private ImageButton voiceAddButton;
private ImageButton quickAddButton;
private EditText quickAddBox;
private Timer backgroundTimer;
@ -164,6 +166,7 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
private final TaskListContextMenuExtensionLoader contextMenuExtensionLoader = new TaskListContextMenuExtensionLoader();
private VoiceInputAssistant voiceInputAssistant;
/* ======================================================================
* ======================================================= initialization
@ -374,6 +377,14 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
}
});
// prepare and set listener for voice add button
voiceAddButton = (ImageButton) findViewById(R.id.voiceAddButton);
int prompt = R.string.TLA_voice_edit_prompt;
if (Preferences.getBoolean(R.string.p_voiceInputCreatesTask, false))
prompt = R.string.TLA_voice_add_prompt;
voiceInputAssistant = new VoiceInputAssistant(this,voiceAddButton,quickAddBox);
voiceInputAssistant.configureMicrophoneButton(prompt);
// set listener for extended add button
((ImageButton)findViewById(R.id.extendedAddButton)).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@ -438,6 +449,12 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
@Override
protected void onResume() {
super.onResume();
if (Preferences.getBoolean(R.string.p_voiceInputEnabled, true) && voiceInputAssistant.isVoiceInputAvailable()) {
voiceAddButton.setVisibility(View.VISIBLE);
} else {
voiceAddButton.setVisibility(View.GONE);
}
registerReceiver(detailReceiver,
new IntentFilter(AstridApiConstants.BROADCAST_SEND_DETAILS));
registerReceiver(detailReceiver,
@ -550,6 +567,17 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// handle the result of voice recognition, put it into the textfield
if (voiceInputAssistant.handleActivityResult(requestCode, resultCode, data)) {
// if user wants, create the task directly (with defaultvalues) after saying it
if (Preferences.getBoolean(R.string.p_voiceInputCreatesTask, false))
quickAddTask(quickAddBox.getText().toString(), true);
super.onActivityResult(requestCode, resultCode, data);
// the rest of onActivityResult is totally unrelated to voicerecognition, so bail out
return;
}
super.onActivityResult(requestCode, resultCode, data);
if(resultCode != RESULT_CANCELED) {
@ -990,5 +1018,4 @@ public class TaskListActivity extends ListActivity implements OnScrollListener,
setUpTaskList();
}
}

@ -0,0 +1,205 @@
package com.todoroo.astrid.voice;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import com.timsu.astrid.R;
import com.todoroo.andlib.utility.Preferences;
/**
* This class handles taking voice-input and appends the text to the registered EditText-instance.
* You can have multiple VoiceInputAssistants per Activity, just use the additional constructor
* to specify unique requestCodes for the RecognizerIntent (e.g. VoiceInputAssistant.VOICE_RECOGNITION_REQUEST_CODE+i).
* If you have only one VoiceInputAssitant on an Activity, just use the normal constructor.
* <p>
* You can query voiceinput-capabilities by calling isVoiceInputAvailable() for external checking,
* but the visibility for the microphone-button specified by the constructor is handled in configureMicrophoneButton(int).
*
* @author Arne Jans
*/
public class VoiceInputAssistant {
/** requestcode for activityresult from voicerecognizer-intent */
public static final int VOICE_RECOGNITION_REQUEST_CODE = 1234;
/**
* This requestcode is used to differentiate between multiple microphone-buttons on a single activity.
* Use the mightier constructor to specify your own requestCode in this case for every additional use on an activity.
* If you only use one microphone-button on an activity, you can leave it to its default, VOICE_RECOGNITION_REQUEST_CODE.
*/
private int requestCode = VOICE_RECOGNITION_REQUEST_CODE;
private final Activity activity;
private final ImageButton voiceButton;
private final EditText textField;
private boolean voiceInputAvailable;
private String languageModel = RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH;
/**
* @param languageModel the languageModel to set
*/
public void setLanguageModel(String languageModel) {
this.languageModel = languageModel;
}
/**
* @return the languageModel
*/
public String getLanguageModel() {
return languageModel;
}
/**
* Creates a new VoiceInputAssistant-instance simply for checking the availability of the
* RecognizerService. This is used for Preferences-Screens that dont want to provide
* a microphone-button themselves.
*/
public VoiceInputAssistant(Activity activity) {
Assert.assertNotNull("Each VoiceInputAssistant must be bound to an activity!", activity);
this.activity = activity;
this.voiceButton = null;
this.textField = null;
}
/**
* Creates a new VoiceInputAssistance-instance for use with a specified button and textfield.
* If you need more than one microphone-button on a given Activity, use the other constructor.
*
* @param activity the Activity which holds the microphone-buttone and the textField to insert recognized test
* @param voiceButton the microphone-Button
* @param textField the textfield that should get the resulttext
*/
public VoiceInputAssistant(Activity activity, ImageButton voiceButton, EditText textField) {
Assert.assertNotNull("Each VoiceInputAssistant must be bound to an activity!", activity);
Assert.assertNotNull("A VoiceInputAssistant without a voiceButton makes no sense!", voiceButton);
Assert.assertNotNull("You have to specify a textfield that is bound to this VoiceInputAssistant!!", textField);
this.activity = activity;
this.voiceButton = voiceButton;
this.textField = textField;
}
/**
* The param requestCode is used to differentiate between multiple
* microphone-buttons on a single activity.
* Use the this constructor to specify your own requestCode in
* this case for every additional use on an activity.
* If you only use one microphone-button on an activity,
* you can leave it to its default, VOICE_RECOGNITION_REQUEST_CODE.
*
*
* @param activity
* @param voiceButton
* @param textField
* @param requestCode has to be unique in a single Activity-context,
* dont use VOICE_RECOGNITION_REQUEST_CODE, this is reserved for the other constructor
*/
public VoiceInputAssistant(Activity activity, ImageButton voiceButton, EditText textField, int requestCode) {
this(activity, voiceButton, textField);
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE)
throw new InvalidParameterException("You have to specify a unique requestCode for this VoiceInputAssistant!");
this.requestCode = requestCode;
}
/**
* Fire an intent to start the speech recognition activity.
* This is fired by the listener on the microphone-button.
*
* @param prompt Specify the R.string.string_id resource for the prompt-text during voice-recognition here
*/
public void startVoiceRecognitionActivity(int prompt) {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, activity.getString(prompt));
activity.startActivityForResult(intent, requestCode);
}
/**
* This callback-method has to be called from Activity.onActivityResult within your activity
* with parameters directly on passthru.<br>
* You can check in your activity if it was really a RecognizerIntent that was handled here,
* if so, this method returns true. In this case, you should call super.onActivityResult in your
* activity.onActivityResult.
* <p>
* If this method returns false, then it wasnt a request with a RecognizerIntent, so you can handle
* these other requests as you need.
*
* @param requestCode if this equals the requestCode specified by constructor, then results of voice-recognition
* @param resultCode
* @param data
* @return
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
boolean result = false;
// handle the result of voice recognition, put it into the textfield
if (requestCode == this.requestCode) {
// this was handled here, even if voicerecognition fails for any reason
// so your program flow wont get chaotic if you dont explicitly state
// your own requestCodes.
result = true;
if (resultCode == Activity.RESULT_OK) {
// Fill the quickAddBox-view with the string the recognizer thought it could have heard
ArrayList<String> match = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
// make sure we only do this if there is SomeThing (tm) returned
if (match != null && match.size() > 0 && match.get(0).length() > 0) {
Editable currentText = textField.getText();
String recognizedSpeech = match.get(0);
if (currentText.length() > 0) {
// if something is already typed in, append the recognized speech,
// add a space if it isn't already there
textField.append((currentText.toString().endsWith(" ") ? recognizedSpeech : " "+recognizedSpeech ));
} else {
textField.setText(recognizedSpeech);
}
}
}
}
return result;
}
/**
* Call this to see if your phone supports voiceinput in its current configuration.
* If this method returns false, it could also mean that Google Voicesearch is simply
* not installed.
* If this method returns true, internal use of it enables the registered microphone-button.
*
* @return whether this phone supports voiceinput
*/
public boolean isVoiceInputAvailable() {
// Check to see if a recognition activity is present
PackageManager pm = activity.getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(
new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
return (activities.size() != 0);
}
public void configureMicrophoneButton(final int prompt) {
if (Preferences.getBoolean(R.string.p_voiceInputEnabled, true) && isVoiceInputAvailable()) {
voiceButton.setVisibility(View.VISIBLE);
voiceButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startVoiceRecognitionActivity(prompt);
}
});
} else {
voiceButton.setVisibility(View.GONE);
}
}
}

@ -0,0 +1,146 @@
/**
*
*/
package com.todoroo.astrid.voice;
import java.util.HashMap;
import java.util.Locale;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import com.todoroo.andlib.service.ContextManager;
/**
* @author Arne Jans
*
*/
public class VoiceOutputAssistant implements OnInitListener {
private static final int MY_DATA_CHECK_CODE = 2534;
private static final String TAG = "Astrid.VoiceOutputAssistant";
private final Context context;
private static VoiceOutputAssistant instance = null;
private TextToSpeech mTts;
private boolean isTTSInitialized;
private boolean retryLastSpeak;
private String textToSpeak;
private static final HashMap<String, String> ttsParams = new HashMap();
static {
ttsParams.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
String.valueOf(AudioManager.STREAM_NOTIFICATION));
}
private VoiceOutputAssistant() {
this.context = ContextManager.getContext().getApplicationContext();
}
public static VoiceOutputAssistant getInstance() {
if (instance == null) {
instance = new VoiceOutputAssistant();
}
return instance;
}
public void checkIsTTSInstalled() {
if (!isTTSInitialized && context instanceof Activity) {
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
((Activity) context).startActivityForResult(checkIntent,
MY_DATA_CHECK_CODE);
}
}
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
// success, create the TTS instance
initTTS();
} else {
// missing data, install it
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
context.startActivity(installIntent);
}
return true;
}
return false;
}
private void initTTS() {
mTts = new TextToSpeech(context, (OnInitListener)this);
}
public void queueSpeak(String textToSpeak) {
if (mTts != null && isTTSInitialized) {
mTts.speak(textToSpeak, TextToSpeech.QUEUE_ADD, ttsParams);
while (mTts.isSpeaking()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
retryLastSpeak = true;
this.textToSpeak = textToSpeak;
initTTS();
}
}
@Override
public void onInit(int status) {
// status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR.
if (status == TextToSpeech.SUCCESS) {
// Set preferred language to US english.
// Note that a language may not be available, and the result will indicate this.
int result = mTts.setLanguage(Locale.getDefault());
// Try this someday for some interesting results.
// int result mTts.setLanguage(Locale.FRANCE);
if (result == TextToSpeech.LANG_MISSING_DATA ||
result == TextToSpeech.LANG_NOT_SUPPORTED) {
// Language data is missing or the language is not supported.
Log.e(TAG, "Language is not available.");
} else {
// Check the documentation for other possible result codes.
// For example, the language may be available for the locale,
// but not for the specified country and variant.
mTts.speak("", 0, null);
// The TTS engine has been successfully initialized.
isTTSInitialized = true;
// if this request came from queueSpeak, then speak it and reset the memento
if (retryLastSpeak && this.textToSpeak != null) {
this.queueSpeak(this.textToSpeak);
retryLastSpeak = false;
textToSpeak = null;
}
}
} else {
// Initialization failed.
Log.e(TAG, "Could not initialize TextToSpeech.");
}
}
/**
* Has to be called in onDestroy of the activity that uses this instance of VoiceOutputAssistant.
*/
public void onDestroy() {
if (mTts != null && isTTSInitialized) {
mTts.shutdown();
mTts = null;
isTTSInitialized = false;
}
}
}
Loading…
Cancel
Save