Create an App Widget in Android with Text-to-Speech (TTS)
DownloadKeywords: AppWidgetProvider RemoteViews AppWidgetManager BroadcastReceiver Widget Configuration Activity AlarmManager TextToSpeech Service PreferenceActivity OnPreferenceChangeListener
Contents- Overview
- Create a new Android project
- Create Vocab App Widget
- Android manifest
- The Widget Provider class
- The Widget layout
- Using AlarmManager to update App Widget
- The Widget Configuration activity
- The Widget Configuration layout
- Android Text-to-Speech (TTS) using Service
- Create a Settings screen
- What's next?
10. Android Text-to-Speech (TTS) using Service
Text-to-Speech functionality enables an Android device to "speak" with help of TTS engine and language specific data. Android provides an easy way of querying the platform for availability of these language files using an Intent.private static final int TTS_CHECK_CODE = 101; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //... usual stuff Intent checkIntent = new Intent(); checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); startActivityForResult(checkIntent, TTS_CHECK_CODE); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == TTS_CHECK_CODE) { if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) { // success, create the TTS instance } else { // missing data, install it Intent installIntent = new Intent(); installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); startActivity(installIntent); } } }If TTS data is missing then in onActivityResult callback we launch an activity that directs user to the Play Store for downloading and installing the data.
If data is already present then we can create the TTS instance and configure it.
You can read more about using TTS from Android developers blogspot.
Since we are going to use a Service for Text-to-Speech implementation so we will initialize TTS inside the service.First, register the service in manifest.
<service android:name=".SpeechService" > </service>Next, create SpeechService class as follows.
public class SpeechService extends Service implements TextToSpeech.OnInitListener { public static final String EXTRA_WORD = "word"; public static final String EXTRA_MEANING = "meaning"; private TextToSpeech tts; private String word, meaning; private boolean isInit; private Handler handler; @Override public void onCreate() { super.onCreate(); tts = new TextToSpeech(getApplicationContext(), this); handler = new Handler(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { handler.removeCallbacksAndMessages(null); word = intent.getStringExtra(SpeechService.EXTRA_WORD); meaning = intent.getStringExtra(SpeechService.EXTRA_MEANING); if (isInit) { speak(); } handler.postDelayed(new Runnable() { @Override public void run() { stopSelf(); } }, 15*1000); return SpeechService.START_NOT_STICKY; } @Override public void onDestroy() { if (tts != null) { tts.stop(); tts.shutdown(); } super.onDestroy(); } @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { int result = tts.setLanguage(Locale.US); if (result != TextToSpeech.LANG_MISSING_DATA && result != TextToSpeech.LANG_NOT_SUPPORTED) { speak(); isInit = true; } } } private void speak() { if (tts != null) { tts.speak(word, TextToSpeech.QUEUE_FLUSH, null); tts.speak(meaning, TextToSpeech.QUEUE_ADD, null); } } @Override public IBinder onBind(Intent arg0) { return null; } }TextToSpeech constructor requires a listener which would receive callback once TTS is ready. So we make SpeechService implement OnInitListener interface and override onInit() method where we configure TTS (e.g. set language) and use it to speak.
Finally, we stop speech service after some time by using a Handler. It is good practice to shut down TTS in onDestroy() method to release resources held by TTS engine.
Now that our speech service is ready, we'll integrate it with our widget.
Modify VocabWidget class as follows.
public static final String ACTION_SPEAKER = "com.appsrox.dailyvocab.action.SPEAKER"; static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { //... // Construct the RemoteViews object RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.vocab_widget); views.setTextViewText(R.id.txtWord, word); views.setTextViewText(R.id.txtMeaning, meaning); boolean isTTS = prefs.getBoolean(SettingsActivity.TTS_PREF, false); if (isTTS) { views.setOnClickPendingIntent(R.id.btnSpeaker, getPendingSelfIntent(context, ACTION_SPEAKER, word, meaning)); views.setViewVisibility(R.id.btnSpeaker, View.VISIBLE); } else { views.setViewVisibility(R.id.btnSpeaker, View.GONE); } //... } private static PendingIntent getPendingSelfIntent(Context context, String action, String... content) { Intent intent = new Intent(context, VocabWidget.class); intent.setAction(action); if (content != null && content.length == 2) { intent.putExtra(SpeechService.EXTRA_WORD, content[0]); intent.putExtra(SpeechService.EXTRA_MEANING, content[1]); } return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } @Override public void onReceive(Context context, Intent intent) { if (ACTION_SPEAKER.equals(intent.getAction())) { Intent speechIntent = new Intent(context, SpeechService.class); speechIntent.putExtra(SpeechService.EXTRA_WORD, intent.getStringExtra(SpeechService.EXTRA_WORD)); speechIntent.putExtra(SpeechService.EXTRA_MEANING, intent.getStringExtra(SpeechService.EXTRA_MEANING)); context.startService(speechIntent); } else if (ACTION_UPDATE.equals(intent.getAction())) { onUpdate(context); } else super.onReceive(context, intent); }We check whether TTS is enabled or not before displaying speaker button on the widget. Clicking this button by user results in starting our SpeechService with the text passed as extras. The service gets automatically stopped as discussed before.