Send SMS Notify Example

This example shows how to send an SMS on a suitably equipped device and receive a notification intent that indicates whether or not the message was sent.

A screenshot of the application.

The example only performs minimal checking to ensure that some sort of phone number is entered by the user and that the message is not empty. It does not check whether the message was sent successfully, nor does it receive a delivery notification.

We begin by importing the classes and modules needed by our application. The most relevant ones for this example are those from the android.telephony module.

from java.lang import String
from android.app import Activity, PendingIntent
from android.content import BroadcastReceiver, Context, Intent, IntentFilter
from android.os import Build
from android.telephony import SmsManager
from android.text import InputType, TextWatcher
from android.view import View, ViewGroup
from android.widget import Button, EditText, LinearLayout, TextView, Toast

We import the the following module so that we can specify the theme of the application.

import android.R

We also use a convenience widget from the serpentine package.

from serpentine.widgets import VBox

We define a class based on the standard Activity class. This represents the application, and will be used to present a graphical interface to the user.

class SendSMSNotifyActivity(Activity):

    __interfaces__ = [TextWatcher, View.OnClickListener]

The initialisation method only needs to call the corresponding method in the base class.

    def __init__(self):
    
        Activity.__init__(self)

The onCreate method is called when the activity is created. Our implementation calls the onCreate method of the base class, queries the available telephony features and displays them in a graphical layout.

    def onCreate(self, bundle):
    
        Activity.onCreate(self, bundle)

We set the theme of the application to the device's default. This tends to make it fit in better with the rest of the user interface.

        self.setTheme(android.R.style.Theme_DeviceDefault)

The user interface is a simple collection of text labels and editors that allow the user to enter a phone number and a message. The message is sent when the user clicks the "Send message" button.

        numberLabel = TextView(self)
        numberLabel.setText("Phone number:")
        self.numberEdit = EditText(self)
        self.numberEdit.setInputType(InputType.TYPE_CLASS_PHONE)
        self.numberEdit.addTextChangedListener(self)
        
        textLabel = TextView(self)
        textLabel.setText("Message:")
        self.textEdit = EditText(self)
        self.textEdit.addTextChangedListener(self)
        
        self.sendButton = Button(self)
        self.sendButton.setText("Send message")
        self.sendButton.setEnabled(False)
        self.sendButton.setOnClickListener(self)

The widgets are added to a convenience widget from the serpentine package.

        vbox = VBox(self)
        vbox.addView(numberLabel)
        vbox.addView(self.numberEdit)
        vbox.addView(textLabel)
        vbox.addView(self.textEdit)
        vbox.addView(self.sendButton)

The VBox widget is used as the main user interface in the activity.

        self.setContentView(vbox)

We create an instance of a class that will listen for broadcasts that are relevant to the activity and we register it with the application using an intent filter so that we only receive intents that we are interested in.

        self.receiver = SMSReceiver(self)
        self.registerReceiver(self.receiver, IntentFilter("SMS Sent"))

We implement the following three methods to implement the TextWatcher interface.

    def afterTextChanged(self, s):
        pass
    
    def beforeTextChanged(self, s, start, count, after):
        pass

The following method is the only one from the TextWatcher interface that we implement in full, checking the contents of the two editors in the user interface and enabling the send button if both of them contain text.

    def onTextChanged(self, s, start, before, count):
    
        if str(CAST(self.numberEdit, TextView).getText()) != "" and \
           str(CAST(self.textEdit, TextView).getText()) != "":
        
            self.sendButton.setEnabled(True)
        else:
            self.sendButton.setEnabled(False)

We implement the following method to implement the View.OnClickListener interface. The method is called when the user clicks the send button.

    def onClick(self, view):

We obtain the number and text that the user entered.

        number = str(CAST(self.numberEdit, TextView).getText())
        text = str(CAST(self.textEdit, TextView).getText())

Since this method can only be called when the number and text editors contain values, we can use those values with the default SmsManager to send a text message. Note that we do not check the validity of the number but we split the message into pieces that are short enough to send.

        smsManager = SmsManager.getDefault()
        
        for message in smsManager.divideMessage(text):

We create a pending intent for each message that we send. This causes the intent it contains to be broadcast when the message has been sent, or if it was not sent for some reason.

            pending = PendingIntent.getBroadcast(self, 0, Intent("SMS Sent"), 0)
            smsManager.sendTextMessage(number, None, message, pending, None)

We disable the send button to prevent the user from accidentally sending the message more than once.

        self.sendButton.setEnabled(False)

We define a method that will handle the response we receive after sending a message. This is not a method to handle the intent we created above, but a method that we call from the receiver class that we created earlier.

    @args(void, [int, String])
    def handleResponse(self, result, message):

If the message was sent successfully, we clear the editors so that the send button remains disabled. Otherwise, re-enable the button so that the user can try again if they want to.

        if result == Activity.RESULT_OK:
            self.numberEdit.setText("")
            self.textEdit.setText("")
        else:
            self.sendButton.setEnabled(True)
        
        Toast.makeText(self, message, Toast.LENGTH_SHORT).show()

The SMSReceiver class is a custom BroadcastReceiver subclass that we use to handle broadcasts involving the intents that we passed to the SmsManager when sending text messages.

For convenience, the initialisation method accepts the activity instance so that it can call its handleResponse method. We could have defined an interface for this purpose and used that, but this approach is sufficient for our example.

class SMSReceiver(BroadcastReceiver):

    @args(void, [SendSMSNotifyActivity])
    def __init__(self, parent):
    
        BroadcastReceiver.__init__(self)
        self.parent = parent

We define a dictionary that maps result codes to strings so that we can report what happened to the messages that were sent.

        self.messages = {
            Activity.RESULT_OK: "Message sent",
            SmsManager.RESULT_ERROR_GENERIC_FAILURE: "Failed to send message",
            SmsManager.RESULT_ERROR_NO_SERVICE: "No service",
            SmsManager.RESULT_ERROR_NULL_PDU: "Failed to send message",
            SmsManager.RESULT_ERROR_RADIO_OFF: "Radio off"
            }

The onReceive method is a standard method that handles incoming broadcasts. Since we only receive intents we are interested in, thanks to the intent filter created earlier, we can pass the result code and an appropriate message to the activity's handleResponse method.

    def onReceive(self, context, intent):
    
        result = self.getResultCode()
        self.parent.handleResponse(result, self.messages[result])

Files