dcsimg

Putting Text to Speech to Work

Raise your (phone's) voice as we build out our Text To Speech app for Android.

Application for TTS

In a prior article we explored using the Text To Speech (TTS) capabilities native to Android.

In this article we begin to apply the TTS capabilities into an application that has (slightly) more utility.

The reasons for using Text To Speech range from the practical, safety-minded applications to the “just for fun”. The application we build in this article is arguably a little bit of both.

While our prior look at Text To Speech was geared around the mechanics of using the TTS features, this application spends a bit more time with the context of the application and leverages what we have previously learned.

Reading your messages

The name of the sample application is “Linux Magazine SMS Reader”, or LMSMSReader.

The application works as follows:

  • When an incoming text message arrives, our application is notified.
  • The application obtains a copy of the inbound message.
  • A new Activity is launched to “process” the message.
  • The “processing” of this message causes the TTS subsystem to “read” the message to us.
  • The incoming text message still winds up in the normal “inbox” as expected.

Before diving into the code, let’s enumerate from a high-level just what we’re going to need here:

  • A BroadcastReceiver class, to be notified of incoming SMS messages.
  • An Activity class to interact with the TTS services
  • Some “glue” to connect the BroadcastReceiver to the Activity.
  • An appropriately apportioned AndroidManifest.xml to wire everything up properly.

Let’s start by examining the code for our implementation of a BroadcastReceiver: LMSMSReader.java.

package com.navitend.lm.smsreader;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import android.os.Bundle;
import android.telephony.SmsMessage;

public class LMSMSReader extends BroadcastReceiver  {

	private final String tag = "LMSMSReader";

	@Override
	public void onReceive(Context context, Intent intent) {
		Log.i(tag,"onReceive invoked!");

		Bundle extras = intent.getExtras();
        if (extras == null) {
        	Log.i(tag,"didn't like this ....message");
        	return;
        }

        Object[] pdus = (Object[]) extras.get("pdus");
        Log.i(tag,"there are [" + pdus.length + "] messages");

        SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[0]);
        String fromAddress = message.getOriginatingAddress();

        Log.i(tag,"message from : " + fromAddress + " " + message.getMessageBody().toString());

        Intent rtmIntent = new Intent();
        rtmIntent.setClass(context, ReadThisMessage.class);
        rtmIntent.putExtra("MESSAGE", message.getMessageBody().toString());
        rtmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |  Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        context.startActivity(rtmIntent);
	}

}

A quick glance at this code reveals that the class LMSMSReader is extending the BroadcastReceiver class. You may recall that the BroadcastReceiver is one of the four main classes that can comprise an Android application. The others are: Actvity, Service, and ContentProvider.

A BroadcastReceiver’s onReceive method is invoked when a particular event occurs. In this case, we are interested in the scenario where a Text (or SMS) message has been received. When we look at the AndroidManifest.xml file in detail we will see how things are wired up. For now, know that the onReceive method of the LMSMSReader class is invoked when an SMS message is received.

The two arguments to the onReceive method include an application Context and an Intent. The Intent contains the information about our message. To obtain the message data itself, we need to extract the “extras” from the Intent with a call to the getExtras() method.

Assuming we don’t have a false alarm (i.e. no Extras), we extract the data labeled as “pdus”. PDU stands for “Protocol Description Unit”. You can learn more on the ‘net. A quick Google search brings up this site which provides some more specifics.

Multiple PDU’s may be found in an incoming message but for our purposes here, we’re just going to work with the first message.

We can extract some data including the sender’s address and the message body. You can learn more about the SMS message class by browsing the documentation.

Now that we’ve got a message, we would like the application to read it to us!

In order to accomplish this we need to package up the message contents into an Intent and then pass this on to our Activity which will take care of the TTS details for us.

We create a new Intent and explicitly tell it that we want to work with the ReadThisMessage class which we’ll review in a moment.

In addition to the class name, we also need to pass along the message contents which we do by adding an “Extra” with a name of “MESSAGE” and the value set to the body of the SMS Message.

The other tailoring we want to perform to our newly created Intent is to set some flags. There are many flags to choose from, so why these two?

A well-behaved BroadcastReceiver does it’s job of reacting to various notifications/stimuli, dispatching its work, and then getting out of the way. The job of this particular BroadcastReceiver implementation is simply to react to the incoming SMS and then pass off the data to our Activity. We want a new task to be created to provide allow the BroadcastReceiver to terminate and then let the newly created Activity do as it wishes so we set the flag named FLAG_ACTIVITY_NEW_TASK. In fact, if you try to call startActivity() without this flag, Android throws an exception.

The other flag we want to set has a little less obvious reason. If you press and hold the “Home” key on your Android device, you will see the most recently run applications. Some might be still running and others are just in the “most recently used”, or “MRU” list. This kind of functionality may or may not be what you want, so you can add or remove the FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS flag. If you want to be able to listen again to the most recently received Text Message, leave this flag out of the call to addFlags(). If you would prefer to not have the application listed in your “Recents”, then add the flag. Your choice.

Now it is time to look at the ReadThisMessage class implementation.

Reading the message

ReadThisMessage.java contains the code which interacts with the TTS service.

package com.navitend.lm.smsreader;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import android.util.Log;
import java.util.HashMap;

public class ReadThisMessage extends Activity implements OnInitListener,OnUtteranceCompletedListener {

	private TextToSpeech tts = null;
	private final String tag = "LMSMSReader";
	private String msg = "";

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent startingIntent = this.getIntent();
        msg = startingIntent.getStringExtra("MESSAGE");
        tts = new TextToSpeech(this,this);
    }

    @Override
    protected void onDestroy() {
    	super.onDestroy();
    	Log.i(tag,"onDestroy");
    	if (tts!=null) {
    		tts.shutdown();
    	}
    }

    // OnInitListener impl
	public void onInit(int status) {
		Log.i(tag,"onInit");
		HashMap hm = new HashMap();
		hm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "smsmessage");
		tts.setOnUtteranceCompletedListener(this);
		tts.speak(msg, TextToSpeech.QUEUE_FLUSH, hm);
	}

	// OnUtteranceCompletedListener impl
	public void onUtteranceCompleted(String utteranceId) {
		Log.i(tag,"onUtteranceCompleted :" + utteranceId);
		tts.shutdown();
		tts = null;
		finish();
	}
}

Note the import statements for the TextToSpeech classes. Also, we have imported the java.util.HashMap which is used to help track when the reading of our message is complete.

This Activity implements two listeners, one called OnInitListener and the other OnUtteranceCompletedListener.

The code for this Activity is fairly straight-forward though it does rely heavily upon the “callback” mechanism associated with the two listeners implemented.

In the onCreate method we extract the Intent that started the activity — we do this via a call to getIntent().

We then extract the “MESSAGE” parameter which is just a String and store it in a class-level variable named msg.

Next, we start-up the TTS functionality by creating an instance of the TextToSpeech class, passing in a Context which is satisfied by the “this” pointer which refers to the Activity. The next required parameter is a reference to a class which implements the OnInitListener. Again, “this” satisfies the requirement.

From here on out we are relying upon the callbacks. First, the onInit method is invoked when the TTS interface is ready.

In onInit() we setup a HashMap which is a simple reference to this message. Ideally we would use a unique identifier for each message to be spoken. In our case we simply pass in a value of “smsmessage” with a key value of TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID.

We tell this TextToSpeech object instance that we want to be notified when any utterances (i.e. speech) are completed. The key/value pair in the HashMap are used to differentiate between multiple utterances.

Then we ask for the speech to be uttered with a call to the speak() method, passing in the msg text, a parameter to flush any waiting phrases and finally our HashMap with the utterance id provided.

When the utterance has completed the onUtteranceCompleted method is invoked and we call the shutdown() method of the TextToSpeech object to properly “cleanup” and unbind our Activity from the TTS service.

In the case that this method is never invoked for whatever reason, we want to be a good Android-citizen and cleanup in the onDestroy method by conditionally calling the shutdown() method as necessary.

When an incoming message is received you should not only hear the message spoken, but also see some relevant action in the logcat.

04-02 17:37:48.374: INFO/LMSMSReader(7651): onReceive invoked!
04-02 17:37:48.374: INFO/LMSMSReader(7651): there are [1] messages
04-02 17:37:48.394: INFO/LMSMSReader(7651): message from : 19734480070What is for dinner?
04-02 17:37:48.674: INFO/LMSMSReader(7651): onInit
04-02 17:37:50.254: INFO/LMSMSReader(7651): onUtteranceCompleted :smsmessage
04-02 17:37:50.394: INFO/LMSMSReader(7651): onDestroy

Plumbing

That’s cool, but how does Android know to call our application when a message arrives? The answer lies in the AndroidManifest.xml file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.navitend.lm.smsreader"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name" >
		<receiver android:name=".LMSMSReader">
			<intent-filter>
				<action android:name="android.provider.Telephony.SMS_RECEIVED"></action>
			</intent-filter>
		</receiver>
	    <activity android:name=".ReadThisMessage" />
    </application>
	<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
</manifest>

The “receiver” tag calls out the BroadcastReceiver and the IntentFilter specifies which event we are interested in.

The permission: “android.permission.RECEIVE_SMS” is also required in order to process the incoming messages.

Lastly, don’t forget to define the Activity “ReadThisMessage”, otherwise it won’t start when asked to read the message!. Note that unlike many Android applications the sole Activity in this application does NOT show up on the home screen and in fact doesn’t even have a UI.

Of course, there is always more to do. I often get automated text messages related to voice mail notifications, backup notifications or after-hours service emergencies, etc. I certainly don’t want everyone of those messages read aloud. Nor do I necessarily want them read while I am sleeping. Or perhaps I do to help me wake up? Regardless of the specifics, it looks like there is more work to do here. Allowing the user to customize the behavior of the application via Android Preferences will be necessary and something we look at in a future article.

Comments on "Putting Text to Speech to Work"

Look into my homepage; buy rap beats online (Melinda)

Also visit my web page: buy beats online, Ola,

Also visit my blog … rap beats – Klara,

my web site :: buy hip hop beats online (Kellie)

my blog: beats for sale (Katharina)

Also visit my blog :: beats for sale (Isabell)

Feel free to surf to my web site – rap beats (Joey)

Here is my web page buy rap beats online (Amelia)

Look at my page :: hip hop beats for sale (Adam)

Feel free to visit my web-site rap beats

Here is my web-site buy beats online (Moses)

Feel free to surf to my website buy rap beats online

It’s hard to find knowledgeable people on this topic, but you
sound like you know what you’re talking about!
Thanks

Look at my page: lawn care companies

I loved as much as you will receive carried out
right here. The sketch is tasteful, your authored subject matter stylish.
nonetheless, you command get bought an edginess over that you wish be delivering the following.

unwell unquestionably come further formerly again as exactly the same nearly
a lot often inside case you shield this hike.

Review my weblog; Orville

Also visit my blog post – buy rap beats (Irwin)

Check out my web site – hip hop beats for sale; Stephaine,

Feel free to visit my webpage: buy rap beats online (Susanna)

Also visit my site hip hop beats for sale (Stanley)

Can you tell us more about this? I’d like to find
out more details.

my site: Watch Now On Vimeo Com

Thank you a bunch for sharing this with all people you actually
recognize what you are speaking about! Bookmarked. Please additionally consult with my website =).
We can have a hyperlink trade agreement among us

my webpage; Websites Designer

Feel free to surf to my blog post; buy rap beats online – Elizbeth,

Excellent post. I was checking constantly this blog and I’m impressed!
Very useful info specifically the last part :)
I care for such info much. I was seeking this particular information for a long time.
Thank you and good luck.

Also visit my blog post: web site

Also visit my homepage hip hop beats for sale (Corinne)

Thanks for the good writeup. It actually was once a enjoyment
account it. Look complex to more added agreeable from you!
By the way, how can we keep up a correspondence?

Here is my web page :: Kaley

my website … buy beats online (Blondell)

Hello would you mind letting me know which hosting company you’re working with?

I’ve loaded your blog in 3 different best web design company
browsers and I must say this blog loads a lot faster then most.

Can you suggest a good hosting provider at a fair price?

Many thanks, I appreciate it!

Thank you, I have recently been looking for info approximately this subject for ages and yours is the greatest
I’ve found out so far. But, what about the conclusion? Are you sure in regards to the source?

my weblog :: Ted

Hello, its nice article regarding media print, we all understand media is a wonderful source of facts.

Also visit my weblog; hibu free listing

My web blog – buy hip hop beats online (oregonlive.com)

my web blog; regal assets review [Bernardo]

Also visit my blog :: reviews of regal assets
- Nathaniel,

My web site :: regal assets reviews – Mickie,

We’re a bunch of volunteers and starting a brand new scheme
in our community. Your site offered us with helpful information to work on. You have done
a formidable task and our whole group will likely be grateful to you.

Also visit my website – website Design rates

I do wish I had done 1 or 2 observe bouquets for myself,
because I assume the flowers may have been positioned in a extra strategic way, but as was our
motto via all of wedding planning (and life)- qué será será!

Check out my page: rap beats for sale (Alanna)

Also visit my blog post rap beats for sale – Dieter,

Check out my homepage :: hip hop beats for sale (Rick)

Have a look at my homepage – buy gold online uk

Feel free to surf to my webpage reviews of regal assets

My web site: regal assets review; Mitchel,

Feel free to visit my weblog :: buy hip hop beats online (Terra)

Here is my website rap beats for sale (Finn)

Here is my weblog … reviews of regal assets (Hildegard)

Also visit my webpage reviews of regal assets; Rosaline,

Leave a Reply