Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Practical: Android SMS Telephony Author: David Sutton Introduction In this week’s practical we will create a simple application that would allow us to run a pub quiz in which communication occurs via SMS messaging. The first thing we will need to do is find out how to simulate telephony in the emulator. Exercise 1: Start Two Emulators Launch Eclipse then start two emulator instances. Make a note of the ports on which the emulators are running. You can find this out by looking at the title bar of the emulator. For instance if the title is “5554:MyEmulator” then the emulator is running on port 5554. Exercise 2: Simulate Telephony via Telnet Open Putty, or whatever command line tool is available on your computer, and establish a telnet connection to the emulator. If you were connecting from the terminal on a Mac, and your emulator was running on port 5554 the command to use would be telnet localhost 5554 Simulate an SMS message using the sms send command. Simulate a call using gsm call. Experiment with other ways of using the sms and gsm commands. You can find more details at http://developer.android.com/tools/devices/emulator.html Exercise 3: Simulate Telephony Using the DDMS Perspective Open the Eclipse DDMS perspective by selecting the menu item Window->Open Perspective->DDMS You should see perspective resembling Figure 1 below. Use the right hand pane to select the emulator with which you wish to operate and the left hand window to simulate reception of SMS and voice calls. To return to the “normal” perspective select Window->Open Perspective->Java Page 1 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Figure 1 The DDMS Perspective Exercise 4: Simulate Telephony by Calling one Emulator from Another You can send an SMS message from one emulator instance to another by using the port number of the second instance as a phone number. You an also initiate voice calls that way, although you cannot actually speak into the phone. Experiment with this technique. Exercise 5: Set up the UI for the Pub Quiz App Create a new Android application project and edit the layout file for its main Activity to give you something similar to Figure 2. If you decided on a LinearLayout then you could use something similar to Table 1 (you would need to define appropriate Strings in the strings.xml file). Page 2 of 12 Oxford Brookes University Practical Figure 2 UI for the Pub Quiz App Page 3 of 12 U08971 Advanced Mobile Software Development Android SMS Telephony Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Table 1 Possible Layout File for Pub Quiz App <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <EditText android:id="@+id/question_edt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="@string/question_str" /> <EditText android:id="@+id/answer_edt" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="@string/answer_str" /> <Button android:id="@+id/send_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/send_str" /> <TextView android:id="@+id/quizzers_txt" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="36dp" android:text="@string/quizzers_str" /> <ListView android:id="@+id/quizzers_lst" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </LinearLayout> Exercise 6: Create a Class to Represent Quizzers Create a class to represent Quizzers, as described in the lecture slides. If you are in a hurry, the code set out in Table 2 would do. Page 4 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Table 2 A Quizzers Class public class Quizzer { private String name; private String origAddress; private int nbrAnswered; private int nbrCorrect; public Quizzer(String name, String origAddress) { this.name = name; this.origAddress = origAddress; } public String getName() { return name; } public String getOrigAddress() { return origAddress; } public void incNbrAnswered() { nbrAnswered++; } public void incNbrCorrect() { nbrCorrect++; } public String toString() { return name + "(" + nbrCorrect + "/" + nbrAnswered + ")"; } } Exercise 7: Add an ArrayList and ArrayAdapter for the Quizzers Add private fields to your main Activity that represent a list of Quizzers, a reference to the ListView declared in the layout file, and an ArrayAdapter so that the ArrayList contents can be shown in the ListView. Then add code to the OnCreate method of the main Activity so that we instantiate these fields and set up the ArrayAdapter correctly. It might be an idea to add a dummy Quizzer to the ArrayList so that you can check that the code works (in the next exercise we will arrange that quizzers can subscribe by SMS message and get added to the ArrayList that way). After you have completed this exercise the code should resemble that set out in Table 3. Page 5 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Table 3 PubQuiz with ArrayList and ArrayAdapter public class PubQuiz extends Activity { private ListView quizzersLst; private ArrayList<Quizzer> quizzers; private ArrayAdapter<Quizzer> adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pub_quiz); quizzersLst = (ListView) findViewById(R.id.quizzers_lst); quizzers = new ArrayList<Quizzer>(); //Test code - we'll delete this later. quizzers.add(new Quizzer("Dummy","0123456789")); adapter = new ArrayAdapter<Quizzer>(this, android.R.layout.simple_list_item_1, quizzers); quizzersLst.setAdapter(adapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { … the rest of the code is unchanged Exercise 8: Add an ArrayList and ArrayAdapter for the Quizzers In this exercise we will modify the application so that quizzers can sign up by sending an SMS message of the form subscribe <team name> Here’s how we do it. 1. Edit the manifest so that the app has permission to receive SMS messages. To do this you add the permission “android.permission.RECEIVE_SMS”. While you are doing this, you might as well add the permission “android.permission.SEND_SMS”. We won’t need that last one yet, but we will soon. 2. Create a BroadcastReceiver inside your onCreate method, as illustrated in the lecture slides. Override the onReceive method of your BroadcastReceiver so that it calls a private method processMessage, which we will define later. 3. Within the onCreate method, register your BroadcastReceiver to receive Intents with the action string “android.provider.Telephony.SMS_RECEIVED”. You do this by calling Page 6 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony the registerReceiver method with an appropriate IntentFilter, as shown in the lecture slides. 4. Within the processMessage method, examine the first word in the message. If it is the word “subscribe” then interpret the rest of the line as a team name, create a new Quizzer object, add it to the ArrayList of quizzers and notify the ArrayAdapter that the content of the ArrayList has changed. Use a ReentrantLock to ensure that the code which updates the ArrayList is thread safe. Once you have done all this, your main Activity code should 5. Run the app and simulate some incoming subscriptions. The UI should look like that shown in. Table 4 Main Activity Code for Exercise 8 import java.util.ArrayList; import java.util.Scanner; import java.util.concurrent.locks.ReentrantLock; import android.os.Bundle; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.telephony.SmsMessage; import android.view.Menu; import android.widget.ArrayAdapter; import android.widget.ListView; public class PubQuiz extends Activity { private static final String SUBSCRIBE_STR = "subscribe"; private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; private ListView quizzersLst; private ArrayList<Quizzer> quizzers; private ArrayAdapter<Quizzer> adapter; private ReentrantLock lock = new ReentrantLock(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pub_quiz); quizzersLst = (ListView) findViewById(R.id.quizzers_lst); quizzers = new ArrayList<Quizzer>(); adapter = new ArrayAdapter<Quizzer>(this, Page 7 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony android.R.layout.simple_list_item_1, quizzers); quizzersLst.setAdapter(adapter); BroadcastReceiver myReceiver = new BroadcastReceiver() @Override public void onReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); Object[] pdus = (Object[]) bundle.get("pdus"); for (Object pduObj:pdus) { byte[] pdu = (byte[]) pduObj; SmsMessage message = SmsMessage.createFromPdu(pdu); processMessage(message); } } }; IntentFilter filter = new IntentFilter(SMS_RECEIVED); registerReceiver(myReceiver, filter); } private void processMessage(SmsMessage message) { String messageText = message.getMessageBody(); Scanner scan = new Scanner(messageText); if(scan.hasNext()) { if (scan.next().equalsIgnoreCase(SUBSCRIBE_STR)) { String name = scan.nextLine().trim(); String origAdress = message.getOriginatingAddress(); Quizzer quizzer = new Quizzer(name, origAdress); lock.lock(); try { quizzers.add(quizzer); adapter.notifyDataSetChanged(); } finally { lock.unlock(); } } } } …. Page 8 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Figure 3 UI After Completing Exercise 8 Exercise 9: Sending Questions We will now modify our app so that it when we press the “Send” button the question entered in the text box at the top of the screen is sent to all subscribed quizzers and the answer is stored so that it can be checked against their subsequent responses. Here is how we can do it 1. Edit the main Activity so as to add private fields that will represent the Send button and the two text boxes above it. Also add an ArrayList to store the answers to the questions. The additional fields should look something like this: private EditText questionEdt; private EditText answerEdt; private Button sendBtn; private ArrayList<String> answers = new ArrayList<String>(); 2. In the onCreate method of the main activity, add code that will assign values to the first of the three fields set out above. The code should look something like this. questionEdt = (EditText) findViewById(R.id.question_edt); answerEdt = (EditText) findViewById(R.id.answer_edt); sendBtn = (Button) findViewById(R.id.send_btn); 3. Add an onClickListener to the “Send” button which calls a private method sendMessages. We will define sendMessages in the next step. The code should look something like this. Page 9 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony sendBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { sendQuestion(); } }); 4. Write the sendMessage method. It should send a message to all subscribed quizzers that consists of a question number followed by the text of the question set out in the edit box at the top of the screen. So for example if the second question to be defined was “What is the capital of Norway” then the subscribed quizzers would be sent a message that reads “2 What is the capital of Norway”. The sendMessage method should also add the answer to the answers ArrayList. Code along the following lines would work private void sendQuestion() { SmsManager manager = SmsManager.getDefault(); String question = questionEdt.getEditableText().toString(); String answer = answerEdt.getEditableText().toString(); answers.add(answer); String sentQuestion = "" + answers.size() + " " + question; for (Quizzer quizzer : quizzers) { manager.sendTextMessage(quizzer.getOrigAddress(), null, sentQuestion, null, null); } } 5. Test your code. You will need to do this using two or more emulator instances. Exercise 10: Processing Answers Update the processMessage method so that you can process messages that consist of an integer followed by an answer to a question. The added code should find out which quizzer the message came from, increment the number of questions that quizzer has attempted and, if the answer is correct, increment the number of correct answers that quizzer has submitted. So that we can find out which quizzer a message comes from, add the following method to the main Activity. private Quizzer getQuizzer(String origAddress) { for (Quizzer q : quizzers) { if (q.getOrigAddress().equals(origAddress)) { return q; } } return null; } Page 10 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony In order to process answers add the bolded code below to the processMessage method. You will notice that there is now some duplicated code in the method, and you may wish to refactor the code to remove that duplication. private void processMessage(SmsMessage message) { String messageText = message.getMessageBody(); Scanner scan = new Scanner(messageText); if (scan.hasNextInt()) { int questionNo = scan.nextInt(); String origAddress = message.getOriginatingAddress(); Quizzer quizzer = getQuizzer(origAddress); if (quizzer != null && questionNo <= answers.size()) { lock.lock(); try { quizzer.incNbrAnswered(); String answer = scan.nextLine().trim(); if (answer.equalsIgnoreCase( answers.get(questionNo - 1).trim())) { quizzer.incNbrCorrect(); } adapter.notifyDataSetChanged(); } finally { lock.unlock(); } } } if (scan.hasNext()) { if (scan.next().equalsIgnoreCase(SUBSCRIBE_STR)) { String origAddress = message.getOriginatingAddress(); String name; if (scan.hasNextLine()) { name = scan.nextLine().trim(); } else { name = origAddress; } Quizzer quizzer = new Quizzer(name, origAddress); lock.lock(); try { quizzers.add(quizzer); adapter.notifyDataSetChanged(); } finally { lock.unlock(); } } } } Page 11 of 12 Oxford Brookes University Practical U08971 Advanced Mobile Software Development Android SMS Telephony Further Work The quiz app is fairly basic, and you could extend it in several ways. Here are a couple of suggestions: Modify the app so that you can end a quiz and start another one. Write an app that could be used by the quizzers as an alternative to just sending their answers by SMS. Page 12 of 12