We talk a lot about how to send SMS Messages from web applications, but how to send an SMS message from Android? There are a couple of extra considerations for that. Still, we’ll be have it shipped in 15 minutes. Let’s go!
The Problem
While Twilio is a REST API and theoretically you could make an HTTP request to it directly, you would need to store your Twilio credentials inside your app which poses a serious security issue. An attacker could decompile the application, extract your credentials and use your Twilio account for anything they liked.
To avoid this we will create a backend application that implements the Twilio REST API, wraps up your credentials and sends SMS messages for you. Then you can call your backend application from your Android application and send SMS messages without distributing your credentials.
Our tools
For our app to be able to send a text message using the Twilio REST API we will need the following:
- Your favourite IDE for Android development. I will be using Android Studio.
- A Twilio Account and a Twilio Phone Number – Sign up for free!
- A secondary choice of language to create our backend. I will use the Twilio Java library with IntelliJ IDEA, but feel free to use any of our other libraries.
- ngrok – You can read more about ngrok here.
If you just want to skip to coding your mobile app, feel free clone this repository with the backend, or just deploy it to Heroku.
You will also find the entire Android application in this repository.
Creating our backend
Because I’ll be using Java to create this backend, I’m gonna start by opening IntelliJ IDEA and creating a new Java project there. We wrote a Getting Started with Gradle and the Spark Framework tutorial in the past in case you need help setting things up.
Now that our project is created, open build.gradle and add the following plugins and dependencies to it.
group 'uk.co.placona'
version '1.0-SNAPSHOT'
buildscript {
repositories { jcenter() }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
}
}
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
mainClassName = 'SMSBackend'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
compile 'com.sparkjava:spark-core:2.6.0'
compile group: 'com.twilio.sdk', name: 'twilio', version: '7.9.0'
compile 'org.slf4j:slf4j-simple:1.6.1'
}
Create a new class in SMSMobileBackend/src/main/java called SMSBackend
and add a Main method to it.
import static spark.Spark.*;
public class SMSBackend {
public static void main(String[] args) {
get("/", (req, res) -> "Hello, World!");
}
}
Run the application by opening the Gradle tab, double-clicking shadowJar and running the generated jar file in SMSMobileBackend/build/libs.
When you load up http://127.0.0.1:4567 on your browser you should see a hello world message.
Let’s make sure this application is also available externally with the help of ngrok so our mobile app can get access to it. In your terminal run:
ngrok http 4567
Now that you know your setup is correct and copied the URL ngrok generated for you, go back to the SMSBackend class and create a new endpoint to handle sending SMS messages. Our android application will then be able send requests to it. Your class should end up like this:
import com.twilio.http.TwilioRestClient;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.rest.api.v2010.account.MessageCreator;
import com.twilio.type.PhoneNumber;
import spark.Spark;
import static spark.Spark.get;
import static spark.Spark.post;
public class SMSBackend {
public static void main(String[] args) {
get("/", (req, res) -> "Hello, World");
TwilioRestClient client = new TwilioRestClient.Builder(System.getenv("TWILIO_ACCOUNT_SID"), System.getenv("TWILIO_AUTH_TOKEN")).build();
post("/sms", (req, res) -> {
String body = req.queryParams("Body");
String to = req.queryParams("To");
String from = YOUR_TWILIO_PHONE_NUMBER;
Message message = new MessageCreator(
new PhoneNumber(to),
new PhoneNumber(from),
body).create(client);
return message.getSid();
});
}
}
Just make sure you replace the Account Sid, Auth Token, and Twilio phone number with the ones you can get from the dashboard. Restart the application and we should be all set with this.
Creating our App
Now that we have our backend running head to Android Studio or your favourite Android IDE and create a new project called Twilio SMS.
When the project is finished loading, open the app’s build.gradle and add the dependency OkHttp to it. OkHttp is an open source project by Square that makes it really easy for us to make an HTTP request to our Java backend.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.squareup.okhttp3:okhttp:3.2.0'
}
When you save the file, your IDE will ask you to sync the dependencies of the project. Go ahead and let it do its thing. Once it completes, you will have the dependency downloaded.
Open AndroidManifest.xml
and add the Internet
permission to your app. We will need that to make HTTP requests to our backend app.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.co.placona.twiliosms">
<uses-permission android:name="android.permission.INTERNET"/>
Let’s create our layout by opening res/layout/activity_main.xml
and add two text fields for telephone and message, and a button to send it.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="uk.co.placona.twiliosms.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Phone Number"
android:id="@ id/textView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@ id/txtNumber"
android:hint=" 44 12345 67890"
android:layout_below="@ id/textView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Message"
android:id="@ id/textView2"
android:layout_below="@ id/txtNumber"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:ems="10"
android:id="@ id/txtMessage"
android:layout_below="@ id/textView2"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignRight="@ id/txtNumber"
android:layout_alignEnd="@ id/txtNumber" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="@ id/btnSend"
android:layout_centerVertical="true"
android:layout_alignRight="@ id/txtMessage"
android:layout_alignEnd="@ id/txtMessage" />
</RelativeLayout>
If you run the application now, you will see that your app looks like this:
If you enter a message and hit Send however, you will notice that nothing happens. That’s because we’re still not making a request to our backend. Let’s do this now.
Open MainActivity.java
and add the following member variables and imports to the top of the class:
package uk.co.placona.twiliosms;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private EditText mTo;
private EditText mBody;
private Button mSend;
private OkHttpClient mClient = new OkHttpClient();
private Context mContext;
In the body of the onCreate method, initialise the widgets so we get references to our UI.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTo = (EditText) findViewById(R.id.txtNumber);
mBody = (EditText) findViewById(R.id.txtMessage);
mSend = (Button) findViewById(R.id.btnSend);
mContext = getApplicationContext();
}
Now create a new method in this class called post
. This method will return the response from the HTTP request we will make to our backend application.
Call post(String url, Callback callback) throws IOException{
RequestBody formBody = new FormBody.Builder()
.add("To", mTo.getText().toString())
.add("Body", mBody.getText().toString())
.build();
Request request = new Request.Builder()
.url(url)
.post(formBody)
.build();
Call response = mClient.newCall(request);
response.enqueue(callback);
return response;
}
The code above will take a URL and a callback as arguments, and will build a new multipart form body for us, which we will then pass along to a new asynchronous request.
The last thing we need to do now is hook this up to our button, so when we press it, the post method gets called and the SMS message is sent.
Back on the onCreate
method, paste the following code just under the send
variable.
mSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
post(mContext.getString("YOUR_NGROK_URL/sms"), new Callback(){
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
runOnUiThread(new Runnable() {
@Override
public void run() {
mTo.setText("");
mBody.setText("");
Toast.makeText(getApplicationContext(),"SMS Sent!",Toast.LENGTH_SHORT).show();
}
});
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
We use the onClick
listener method on the button to to know when to make a request to our post method.
Run the application again, and when the Send button is clicked we pass on the URL we want to post our data to, and upon getting a callback we either show an error message, or display a Toast on the screen saying the SMS message was sent.
Your app is now safe
There’s a warm feeling about knowing that we were able to squash at least one vulnerability from our app by just following a few simple steps. It is also great to now have a backend we can use with other other platforms as we can just perform direct HTTP requests to it.
Using a pattern like this, we could also add phone number lookups to an application or generate phone calls. If you want to use IP Messaging, Video or Client, you’ll want a server to generate access tokens for those services too. Get your backend right and you’re ready to do anything with Twilio in your application.
I would love to know more about the apps you’re building. Hit me up on Twitter @marcos_placona or by email on marcos@twilio.com to tell me more about them.