Login     Sign Up
Cyril Sermon (@admin)
9 months ago
47 Views

Simulating Incoming SMS Messages

There are two techniques available for simulating incoming SMS messages in the emulator. The first was described previoulsy in this section; you can send an SMS message from one emulator to another by using its port number as the destination address.

Alternatively, you can use the Android debug tools introduced in Chapter 2 to simulate incoming SMS messages from arbitrary numbers, as shown in Figure 9-2.

Handling Data SMS Messages

For security reasons, the version 1 release has restricted access to receiving data messages. The following section has been left to indicate how likely future func-tionality may be made available.

Data messages are received in the same way as a normal SMS text message and are extracted in the same way as shown in the above section.

To extract the data transmitted within a data SMS, use the getUserData and getUserDataHeader methods, as shown in the following snippet:

byte[] data = msg.getUserData();

SmsHeader header = msg.getUserDataHeader();

getUserDataHeader returns an array of metadata elements used to describe the data contained in the message.

Emergency Responder SMS Example

In this example, you’ll create an SMS application that turns an Android phone into an emergency response beacon.

Once finished, the next time you’re in unfortunate proximity to an alien invasion or find yourself in a robot-uprising scenario, you can set your phone to automatically respond to your friends’ and family members’ pleas for a status update with a friendly message (or a desperate cry for help).

To make things easier for your would-be saviors, you’ll use location-based services to tell your rescu-ers exactly where to find you. The robustness of SMS network infrastructure makes SMS an excellent option for applications like this where reliability and accessibility are critical.

Start by creating a new EmergencyResponder project that features an EmergencyResponder Activity.

package com.paad.emergencyresponder;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Locale;

import java.util.concurrent.locks.ReentrantLock;

import java.util.List;

import android.app.Activity;

import android.app.PendingIntent;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.content.BroadcastReceiver;

import android.content.SharedPreferences;

import android.location.Address;

import android.location.Geocoder;

import android.location.Location;

import android.location.LocationManager;

import android.os.Bundle;

import android.telephony.gsm.SmsManager;

import android.telephony.gsm.SmsMessage;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.CheckBox;

import android.widget.ListView;

public class EmergencyResponder extends Activity {

@Override
public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

}

}

Add permissions for finding your location as well as sending and receiving incoming SMS mes-sages to the project manifest.

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android” package=”com.paad.emergencyresponder”>

<application android:icon=”@drawable/icon” android:label=”@string/app_name”>

<activity android:name=”.EmergencyResponder”

android:label=”@string/app_name”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” /> <category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

</application>

<uses-permission

android:name=”android.permission.ACCESS_GPS”/>

<uses-permission

android:name=”android.permission.ACCESS_LOCATION”/>

<uses-permission

android:name=”android.permission.RECEIVE_SMS”/>

<uses-permission android:name=”android.permission.SEND_SMS”/>

</manifest>

Modify the main.xml layout resource. Include a List View to show the people requesting a status update and a series of buttons that users can press to send response SMS messages. Use external resource references to fill in the button text; you’ll create them in Step 4.

<?xml version=”1.0” encoding=”utf-8”?>

<RelativeLayout

xmlns:android=”http://schemas.android.com/apk/res/android” android:layout_width=”fill_parent” android:layout_height=”fill_parent”> <TextView

android:id=”@+id/labelRequestList” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:text=”These people want to know if you’re ok” android:layout_alignParentTop=”true”/>

<LinearLayout

android:id=”@+id/buttonLayout”
xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:padding=”5px”

android:layout_alignParentBottom=”true”>

<CheckBox

android:id=”@+id/checkboxSendLocation”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Include Location in Reply”/>

<Button

android:id=”@+id/okButton” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:text=”@string/respondAllClearButtonText”/>

<Button

android:id=”@+id/notOkButton” android:layout_width=”fill_parent” android:layout_height=”wrap_content” android:text=”@string/respondMaydayButtonText”/>

<Button

android:id=”@+id/autoResponder”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Setup Auto Responder”/>

</LinearLayout>

<ListView

android:id=”@+id/myListView”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

android:layout_below=”@id/labelRequestList”

android:layout_above=”@id/buttonLayout”/>

</RelativeLayout>

Update the external strings.xml resource to include the text for each button and default response messages to use when responding, with “I’m safe” or “I’m in danger” messages. You should also define the incoming message text to use when detecting requests for status responses.

<?xml version=”1.0” encoding=”utf-8”?>

<resources>

<string name=”app_name”>Emergency Responder</string>

<string name=”respondAllClearButtonText”>I am Safe and Well</string> <string name=”respondMaydayButtonText”>MAYDAY! MAYDAY! MAYDAY!</string>

<string name=”respondAllClearText”>I am safe and well. Worry not!</string> <string name=”respondMaydayText”>Tell my mother I love her.</string>

<string name=”querystring”>are you ok?</string> </resources>

At this point, the GUI will be complete, so starting the application should show you the screen shown in Figure 9-3.

Create a new Array List of Strings within the EmergencyResponder Activity to store the phone numbers of the incoming requests for your status. Bind the Array List to the List View, using an Array Adapter in the Activity’s onCreate method, and create a new ReentrantLock object to ensure thread safe handling of the Array List.

Take the opportunity to get a reference to the Check Box and to add Click Listeners for each of the response buttons. Each button should call the respond method, while the Setup Auto Responder button should call the startAutoResponder stub.

ReentrantLock lock;

CheckBox locationCheckBox;

ArrayList<String> requesters;

ArrayAdapter<String> aa;

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

lock = new ReentrantLock();

requesters = new ArrayList<String>();

wireUpControls();

}

private void wireUpControls() {

locationCheckBox = (CheckBox)findViewById(R.id.checkboxSendLocation);

ListView myListView = (ListView)findViewById(R.id.myListView);

int layoutID = android.R.layout.simple_list_item_1;

= new ArrayAdapter<String>(this, layoutID, requesters); myListView.setAdapter(aa);

Button okButton = (Button)findViewById(R.id.okButton); okButton.setOnClickListener(new OnClickListener() {

public void onClick(View arg0) { respond(true, locationCheckBox.isChecked());

}

});

Button notOkButton = (Button)findViewById(R.id.notOkButton); notOkButton.setOnClickListener(new OnClickListener() {

public void onClick(View arg0) { respond(false, locationCheckBox.isChecked());

}

});

Button autoResponderButton = (Button)findViewById(R.id.autoResponder); autoResponderButton.setOnClickListener(new OnClickListener() {

public void onClick(View arg0) {

startAutoResponder();

}

});

}

public void respond(boolean _ok, boolean _includeLocation) {}

private void startAutoResponder() {}

Next, implement a Broadcast Receiver that will listen for incoming SMS messages.

7.1 Start by creating a new static string variable to store the incoming SMS message intent action.

public static final String SMS_RECEIVED =

“android.provider.Telephony.SMS_RECEIVED”;

7.2 Then create a new Broadcast Receiver as a variable in the EmergencyResponder Activity. The receiver should listen for incoming SMS messages and call the requestRecieved method when it sees SMS messages containing the “are you safe” String you defined as an external resource in Step 4.

BroadcastReceiver emergencyResponseRequestReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context _context, Intent _intent) { if (_intent.getAction().equals(SMS_RECEIVED)) {

String queryString = getString(R.string.querystring);

Bundle bundle = _intent.getExtras();

if (bundle != null) {
Object[] pdus = (Object[]) bundle.get(“pdus”); SmsMessage[] messages = new SmsMessage[pdus.length]; for (int i = 0; i < pdus.length; i++)

messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);

for (SmsMessage message : messages) {

if (message.getMessageBody().toLowerCase().contains(queryString)) { requestReceived(message.getOriginatingAddress());

}

}

}

}

}

};

public void requestReceived(String _from) {}

Update the onCreate method of the Emergency Responder Activity to register the Broadcast Receiver created in Step 7.

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

lock = new ReentrantLock();

requesters = new ArrayList<String>();

wireUpControls();

IntentFilter filter = new IntentFilter(SMS_RECEIVED); registerReceiver(emergencyResponseRequestReceiver, filter);

}

Update the requestReceived method stub so that it adds the originating number of each status request’s SMS to the “requesters” Array List.

public void requestReceived(String _from) {

if (!requesters.contains(_from)) {

lock.lock();

requesters.add(_from);

aa.notifyDataSetChanged();

lock.unlock();

}

}

The Emergency Responder Activity should now be listening for status request SMS messages and adding them to the List View as they arrive. Start the application and use the DDMS emulator control to simulate incoming SMS messages, as shown in Figure 9-4.

Now update the Activity to let users respond to these status requests.

Start by completing the respond method stub you created in Step 6. It should iterate over the Array List of status requesters and send a new SMS message to each. The SMS message text should be based on the response strings you defined as resources in Step 4. Fire the SMS using an overloaded respond method that you’ll complete in the next step.

public void respond(boolean _ok, boolean _includeLocation) { String okString = getString(R.string.respondAllClearText); String notOkString = getString(R.string.respondMaydayText);

String outString = _ok ? okString : notOkString;

ArrayList<String> requestersCopy = (ArrayList<String>)requesters.clone();

for (String to : requestersCopy)

respond(to, outString, _includeLocation);

}

private void respond(String _to, String _response, boolean _includeLocation) {}

Update the respond method that handles the sending of each response SMS.

Start by removing each potential recipient from the “requesters” Array List before sending the SMS. If you are responding with your current location, use the Location Manager to find it before sending a second SMS with your current position as raw longitude/latitude points and a geocoded address.

public void respond(String _to, String _response, boolean _includeLocation) {

Remove the target from the list of people we need to respond to. lock.lock();

requesters.remove(_to); aa.notifyDataSetChanged();

13.1. Start by creating a new public static String in the Emergency Responder Activity to be used as a local “SMS Sent” action.

public static final String SENT_SMS = “com.paad.emergencyresponder.SMS_SENT”;

13.2. Update the respond method to include a new PendingIntent that broadcasts the action created in the previous step when the SMS transmission has completed. The packaged Intent should include the intended recipient’s number as an extra.

public void respond(String _to, String _response, boolean _includeLocation) {

Remove the target from the list of people we need to respond to. lock.lock();

requesters.remove(_to); aa.notifyDataSetChanged(); lock.unlock();

SmsManager sms = SmsManager.getDefault();

Intent intent = new Intent(SENT_SMS);

intent.putExtra(“recipient”, _to);

PendingIntent sentIntent = PendingIntent.getBroadcast(getApplicationContext(), 0,

intent,

0);

// Send the message

sms.sendTextMessage(_to, null, _response, sentIntent, null);

StringBuilder sb = new StringBuilder();

if (_includeLocation) {

… existing respond method that finds the current location … ] ArrayList<String> locationMsgs = sms.divideMessage(sb.toString()); for (String locationMsg : locationMsgs)

sms.sendTextMessage(_to, null, locationMsg, sentIntent, null);

}

}

13.3. Then implement a new Broadcast Receiver to listen for this broadcast Intent. Override its onReceive handler to confirm that the SMS was successfully delivered; if it wasn’t, then add the intended recipient back on to the requesters Array List.

private BroadcastReceiver attemptedDeliveryReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context _context, Intent _intent) { if (_intent.getAction().equals(SENT_SMS)) {

if (getResultCode() != Activity.RESULT_OK) {

String recipient = _intent.getStringExtra(“recipient”); requestReceived(recipient);

}

}

}

};

13.4. Finally, register the new Broadcast Receiver by extending the onCreate method of the Emergency Responder Activity.

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

lock = new ReentrantLock();

requesters = new ArrayList<String>();

wireUpControls();

IntentFilter filter = new IntentFilter(SMS_RECEIVED); registerReceiver(emergencyResponseRequestReceiver, filter);

IntentFilter attemptedDeliveryfilter = new IntentFilter(SENT_SMS); registerReceiver(attemptedDeliveryReceiver, attemptedDeliveryfilter);

}

You can now run the application. To test it, you need to open two emulator instances with the applica-tion running in one of them.

Use the DDMS emulator controls to mimic sending an “are you safe” message from one emulator to the other (using its port number as the originating number). When you press one of the response buttons, you should see a new SMS message appear in the mimicked emulator.