Senden einer SMS über Android

May 19, 2016
Autor:in:

Senden einer SMS über Android


Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von How to Send an SMS from Android. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an help@twilio.com freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)

Die Vorgehensweise zum Senden von SMS-Nachrichten über Webanwendungen wird häufig behandelt, aber wie gehen wir beim Senden einer SMS-Nachricht über Android vor? Dabei müssen wir uns zwar mit einigen besonderen Überlegungen auseinandersetzen, aber insgesamt sollte alles innerhalb von 15 Minuten erledigt sein. Legen wir los.

Das Problem

Twilio ist zwar eine REST-API, an die wir theoretisch direkt eine HTTP-Anfrage senden könnten, aber das hieße auch, dass wir unsere Twilio-Anmeldeinformationen in der App speichern müssten, was ein erhebliches Sicherheitsrisiko darstellt. Ein Angreifer könnte die Anwendung dekompilieren, die Anmeldeinformationen extrahieren und unser Twilio-Konto für jeden beliebigen Zweck missbrauchen.

Um dieses Risiko zu vermeiden, erstellen wir eine Back-End-Anwendung, die die Twilio-REST-API implementiert, unsere Anmeldeinformationen umschließt und SMS-Nachrichten für uns sendet. Dann können wir unsere Back-End-Anwendung über die Android-Anwendung aufrufen und SMS-Nachrichten senden, ohne unsere Anmeldeinformationen preiszugeben.

Unsere Tools

Damit unsere App eine Textnachricht über die Twilio-REST-API senden kann, benötigen wir Folgendes:

Wenn du direkt zum Programmieren der mobilen App übergehen möchtest, dann klone dieses Repository mit dem Back-End oder stelle es einfach auf Heroku bereit.

button

Die vollständige Android-Anwendung findest du in diesem Repository.

Erstellen des Back-Ends

Da ich Java zum Erstellen dieses Back-Ends verwende, öffne ich zuerst IntelliJ IDEA und erstelle dort ein neues Java-Projekt. Falls du beim Einrichten Hilfe benötigst, sieh dir in unserem Archiv das Tutorial Erste Schritte mit Gradle und dem Spark-Framework an.

New Gradle Project

Nachdem wir unser Projekt erstellt haben, öffnen wir build.gradle und fügen die folgenden Plug-ins und Abhängigkeiten hinzu.

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'
}

Wir erstellen eine neue Klasse unter SMSMobileBackend/src/main/java mit dem Namen SMSBackend und fügen ihr eine Main-Methode hinzu.

import static spark.Spark.*;

public class SMSBackend {
   public static void main(String[] args) {
       get("/", (req, res) -> "Hello, World!");
   }
}

Jetzt führen wir die Anwendung aus. Dazu öffnen wir die Registerkarte Gradle , doppelklicken auf shadowJar und führen die erzeugte JAR-Datei unter SMSMobileBackend/build/libs aus.

run-app

Wenn wir http://127.0.0.1:4567 im Browser laden, sollten wir eine hello world-Nachricht sehen.

Mithilfe von ngrok sorgen wir dafür, dass diese Anwendung auch extern verfügbar ist und unsere mobile App darauf zugreifen kann. Im Terminal führen wir Folgendes aus:

ngrok http 4567

ngrok

Nachdem wir die richtige Einrichtung geprüft und die von ngrok erzeugte URL kopiert haben, gehen wir zurück zur SMSBackend-Klasse und erstellen einen neuen Endpunkt, der das Senden von SMS-Nachrichten verarbeitet. Unsere Android-Anwendung kann dann Anfragen an diesen Endpunkt senden. Die Klasse sollte in etwa so aussehen:
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();
        });
   }
}

Wir dürfen nicht vergessen, die Konto-SID, das Authentifizierungstoken und die Twilio-Telefonnummer durch die Werte aus dem Dashboard zu ersetzen. Wenn wir die Anwendung jetzt neu starten, sollte alles für den nächsten Schritt bereitstehen.

Erstellen der App

Nachdem unser Back-End jetzt ausgeführt wird, rufen wir Android Studio oder unsere bevorzugte Android-IDE auf und erstellen ein neues Projekt mit dem Namen Twilio SMS.

twilio-sms-android

Wenn das Projekt vollständig geladen ist, öffnen wir die build.gradle-Datei der App und fügen die Abhängigkeit OkHttp hinzu. OkHttp ist ein Open-Source-Projekt von Square, über das wir recht einfach eine HTTP-Anfrage an unser Java-Back-End senden können.

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'
}

Beim Speichern werden wir von der IDE aufgefordert, die Abhängigkeiten des Projekts zu synchronisieren. Das erfolgt automatisch, wenn wir unser OK dazu geben. Nachdem dieser Vorgang abgeschlossen ist, haben wird die Abhängigkeit heruntergeladen.

Wir öffnen AndroidManifest.xml und fügen unserer App die Berechtigung Internet hinzu. Die Berechtigung ist erforderlich, damit wir HTTP-Anfragen an unser Back-End senden können.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="uk.co.placona.twiliosms">

   <uses-permission android:name="android.permission.INTERNET"/>

Wir erstellen jetzt das Layout. Dazu öffnen wir res/layout/activity_main.xml und fügen ein Textfeld für die Telefonnummer, ein Textfeld für die Nachricht und eine Schaltfläche zum Senden der Nachricht hinzu.

<?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>

Wenn wir die Anwendung jetzt ausführen, sollte die App so aussehen:

appli screenshot

Wenn wir allerdings eine Nachricht eingeben und auf Send drücken, geschieht erstmal gar nichts. Grund ist, dass wir noch keine Anfrage an unser Back-End senden. Das ändern wir jetzt.

Wir öffnen MainActivity.java und fügen oberhalb der Klasse folgende Membervariablen und Importe hinzu:

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;

Im Textkörper der onCreate-Methode initialisieren wir die Widgets, damit wir Verweise auf unsere Benutzeroberfläche erhalten.

@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();
}

Jetzt erstellen wir eine neue Methode in dieser Klasse mit dem Namen post. Diese Methode gibt die Antwort auf die HTTP-Anfrage zurück, die wir an unsere Back-End-Anwendung senden.

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;
}

Der oben dargestellte Code benötigt eine URL und einen Rückruf als Argumente und erstellt einen neuen mehrteiligen Formulartextkörper, den wir dann an eine neue asynchrone Anfrage übergeben.

Als Letztes müssen wir eine Verbindung zu unserer Schaltfläche herstellen, damit beim Drücken der Schaltfläche die post-Methode aufgerufen und die SMS-Nachricht gesendet wird.

Zurück in der onCreate-Methode fügen wir direkt unterhalb der send-Variable den folgenden Code ein.

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();
        }
    }
});

Wir verwenden die onClick-Listener-Methode für die Schaltfläche, damit sie weiß, wann sie eine Anfrage an die post-Methode senden soll.

Wir führen die Anwendung erneut aus. Klicken wir jetzt auf die Schaltfläche Send, wird die URL, an die die Daten gesendet werden sollen, übergeben. Beim Eingehen eines Rückrufs zeigen wir entweder eine Fehlermeldung oder ein Popup auf dem Bildschirm an, das besagt, dass die SMS-Nachricht gesendet wurde.

ahoy-from-twilio

Die App ist jetzt sicher

Was für ein beruhigendes Gefühl, ein Sicherheitsrisiko in der App mit ein paar einfache Schritten ausmerzen zu können. Außerdem haben wir jetzt ein Back-End, das wir auf anderen Plattformen verwenden und an das wir direkte HTTP-Anfragen senden können.

Anhand dieses Musters könnten wir auch eine Telefonnummernsuche einer App hinzufügen oder Telefonanrufe erzeugen. Wenn wir IP-MessagingVideo oder Client verwenden möchten, benötigen wir außerdem einen Server zum Erzeugen von Zugriffstoken für diese Dienste. Wenn wir das Back-End richtig konfigurieren, dann stehen uns mit Twilio in unserer Anwendung alle Türen offen.

Ich würde gerne mehr über die Apps erfahren, die du gerade erstellst. Du findest mich auf Twitter @marcos_placona oder sende eine E-Mail an marcos@twilio.com.