Erstellung einer PhoneGap-basierten App zur Annahme und Darstellung von Push-Notifikationen

Push Notifications mit PhoneGap
Kommentare

Mit zunehmender Verbreitung der mobilen Endgeräte und der damit einhergehenden permanenten Erreichbarkeit der Enduser, erfreuen sich „Mobile Apps“ immer höherer Beliebtheit. Sie sind mittlerweile so gestrickt, dass sie, einmal installiert, den Benutzer informieren, sofern bestimme Ereignisse in bevorzugten Anwendungen, wie Facebook, WhatsApp oder auch eDarling eintreten. Dieser Artikel beschreibt eine App, die, nachdem einmal bei Googles GCM-Dienst angemeldet, von Dritten mit Push-Notifikationen „gefüttert“ werden kann.

Das PhoneGap-(PG-)Framework ermöglicht es auf Basis von HTML, CSS und JavaScript Hybride-Anwendungen zu entwickeln und unter dem Mantel einer nativen App zu betreiben. PG baut dabei auf den von Sun Microsystems für Java erfundenen Slogan „Write once, run everywhere“ auf. Eine Standard-PG-App kann mithilfe der PG-eigenen, Cloud-basierten Build-Umgebung für aktuell bis zu sieben verschiedene Endgeräteplattformen gebaut und bereitgestellt werden. Es stellt Schnittstellen zu verschiedensten Hardwarekomponenten des jeweiligen Endgerätes bereit, die der Benutzer über einfache JavaScript-basierte APIs verwenden kann wie z. B. den Kompass, das Telefonbuch sowie das GPS-System. Für Anwendungsfälle, in denen das bereitgestellte API nicht ausreicht, kann der Entwickler so genannte Plug-ins erstellen. Ein Plug-in ermöglicht es dem Entwickler aus einer HTML-Seite heraus Hardwarekomponenten bzw. native Dienste des Endgerätes anzusprechen und zu verwenden. Für unser Beispiel erstellen wir ein Plug-in, das unser Endgerät bei dem GCM-Dienst anmeldet und Notifikationen entgegen nimmt.

Google Cloud Messaging

Google Clound Messaging (GCM) ist ein Google-Dienst, der es ermöglicht Informationen aus externen Anwendungen an Apps auf Android-Endgeräte zu schicken. Die Nachrichten können dabei eine Größe von bis zu 4 KB haben. Normalerweise sind die Nachrichten jedoch wesentlich kürzer, denn sie sollen dem Benutzer nur mitteilen, dass sich ein Status geändert hat und bitten ihn damit indirekt, die App wieder zu öffnen. Google biete dazu eine recht gute Anleitung.

Installation

An dieser Stelle ist es sinnvoll den Quellcode für unser Plug-in von GitHub zu „pullen“ und in einer IDE zu starten. Um den Code lauffähig zu bekommen, wird eine PhoneGap-Version benötigt. Das geladene Zip-File enthält die Bibliotheken für mehrere Umgebungen. Aus dem Android-Verzeichnis benötigen wir die cordova-VERSION.jar, die in das libs-Verzeichnis gelegt werden muss. Ferner nehmen wir die cordova-VERSION.js-Datei und legen sie in das assets/www-Verzeichnis. Zu diesem Zeitpunkt sollte die Anwendung kompilierbar sein. Neben dem Plug-in-Quellcode benötigen wir einen Google Account, der für den GCM-Dienst freigeschaltet werden muss. Google bietet eine sehr gute Dokumentation für die Freischaltung des GCM-Dienstes. Im Verlauf des Dokumentes werden wir die Projekt-ID sowie den Server-API-Key benötigen. Auf beide Token wird in dem Dokument eingegangen (Abb. 1).

Abb. 1: Der rote Balken verdeckt die Projekt-ID, der blaue Balken den API-Key

GCM und sein Vorgänger

Google Cloud Messaging ist der Nachfolger von C2DM [5], dem Cloud-to-Device-Messaging-Dienst. Das unter GitHub abgelegte Plug-in arbeitete in seiner ersten Version noch mit dem C2DM-Dienst. Laut Googles Migrationsdokument arbeitet die neue Version ressourcenschonender, bietet ein besseres API und erspart eine längere Registrierung bei Google für den Dienst.


Die PhoneGap-Anwendung

Für unsere App erstellen wir ein PG-Plug-in. Dieses wird dafür sorgen, dass unser Gerät bei dem GCM-Dienst freigeschaltet wird. Weiterhin wird es sämtliche Notifikationen annehmen und verteilen. Ein PG-Plug-in setzt sich aus folgenden Teilen zusammen:

  1. JavaScript-Datei (der Frontend-Teil des Plug-ins)
  2. Java-Datei (der Backend-Teil des Plug-ins)
  3. Erweiterung der res/xml/plugins.xml (Registry)
  4. Anpassung der Haupt-Activity

Unsere Plug-in-JS-Datei trägt den Namen NPNPlugin.js und liegt zusammen mit den anderen HTML- und JavaScript-Dateien unter assets/www. Wie in Listing 1 dargestellt, meldet sich das Plug-in bei der Initialisierung bei PhoneGap an und ruft direkt den Backend Part des Plug-ins auf. Die weiteren Funktionen in der Datei dienen der Kommunikation mit der Java-Schicht. Exemplarisch für alle Funktionen beschreibe ich deren Syntax anhand:

...
PhoneGap.addConstructor(function(){
    console.log("register NativePushNotification plugin @ PG");
    if(!window.plugins){
        window.plugins = {};
    }
    window.plugins.NPNPlugin = new NPNPlugin();
    PhoneGap.exec(null,null,"NPNPlugin","bootstrap",[]);
});

In Zeile 4 prüfen wir gegen die übergebene Aktion (activatePush). Über die activatePush-Methode registrieren wir unsere App beim GCM-Dienst. In Zeile 7 bereiten wir die Antwort für die JS-Schicht vor. Das PluginResult ist ein PG-Objekt, das die „Antwort“ für die JavaScript-Schicht zusammenbaut.

Arbeitsweise des PluginResults

Um ein tieferes Verständnis für die Arbeitsweise der PluginResult-Klasse zu erhalten, lohnt sich ein Blick in die Original Sourcen. Beispielhaft sei hier die toSuccessCallString-Methode aufgeführt (Listing 3).

...
public String toSuccessCallbackString(String callbackId) { 
    return "try { PhoneGap.callbackSuccess('"+callbackId+"', " + this.getJSONString() + "); } catch(e) { alert('error in callbackSuccess:' + e.message); }";
}
...

Die toSuccessCallString-Methode baut aus übergebenen Werten einen JS-parsbaren String zusammen, der dann von der JavaScript Engine des Endgerätes zur Ausführung gebracht wird. Auf diese Art und Weise erfolgt der Rücksprung in das Frontend unserer Anwendung.

In Zeile 8 von Listing 2 sorgen wir dafür, dass die Callback-ID auf JS-Seite erneut verwendet werden kann. Fehlt dieser Eintrag, so wird die Callback-ID nach jeder Ausführung aus einer JS internen Map entfernt und ist somit nicht mehr nutzbar. Verbinden wir uns mit dem GCM-Dienst, so müssen wir die Projekt-ID übergeben. Der Wert dafür befindet sich in der NPNPlugin.java-Klasse. Der aktuelle Wert (42) ist wie folgt zu ersetzen:

// GCM project id
public final static String PROJECT_ID = "42";

Als Nächstes wird in der res/xml/plugins.xml-Datei der Eintrag für unser Plug-in hinzugefügt. Diese Datei identifiziert sämtliche PG-Plug-ins und zeigt auf deren Java-Implementierung. Was jetzt noch fehlt ist die Einbettung von PG in unsere Haupt-Activity. Listing 4 illustriert das.

1. public class NPNActivity extends DroidGap{
2. ...
3. public void onCreate(Bundle b){
4.        super.onCreate(b);
5.        super.loadUrl("file:///android_asset/www/index.html");
6. ...

Die Start-Activity leitet sich jetzt von der DroidGap ab und integriert damit das PG-Framework. In der Zeile 5 übergeben wir dem Framework die Datei, die als Startseite fungieren soll, und starten so die PG-App. Damit ist unser PG-Plug-in von der Sache her beschrieben. Abbildung 2 zeigt das Sequenzdiagramm für die Aktivierung der Push Notification, das mit ArgoUML erstellt wurde.

Abb. 2: Sequenzdiagamm für „Aktiviere die Push-Notifikationen“

<receiver android:name=".NPNReceiver" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE"<>/action> <action android:name="com.google.android.c2dm.intent.REGISTRATION"<>/action> <category android:name="com.pirack.mobile"></category> </intent-filter> </receiver>

Neben der permission, sich am GCM-Dienst zu registrieren, kann der NPNReceiver auch Nachrichten entgegennehmen. Schauen wir uns den NPNReceiver doch mal aus der Nähe an. Mit dem bisher gewonnenen Know-how in Bezug auf PG-Kommunikation durch die Schichten, haben wir den Großteil der Arbeit bereits hinter uns. Die zentrale Methode eines BroadcastReceiver ist die onReceive(Context context, Intent intent)-Methode. Hier schlagen auch die Notifikationen von dem GCM-Dienst auf. Bei eingehenden Notifikationen kann die App in den Zuständen online oder offline sein. Für jeden dieser Zustände gibt es einen separaten Flow. Ist die App im Vordergrund, wird an die Methode deliverNotification(Bundle b) in der Klasse NPNPlugin die Notifikation mit dem key: msg übertragen. Aus der Methode heraus wird die JavaScript Engine getriggert. Erwähnenswert in diesem Zusammenhang ist, dass wir für eingehende Notifizierungen in der NPNPlugin einen festen JS-Rücksprungpunkt definiert haben (window.plugins.NPNPlugin.notificationCallback(%s);). Ist die App im Hintergrund, werden folgende Aktionen ausgeführt:

  1. Wir fügen in der System-Statusbar des Endgerätes ein Notification-Icon hinzu (Listing 6).
  2. Wir stellen sicher, dass die App gestartet wird, sobald das Icon geklickt wird (Listing 7).
  3. Wir erstellen eine Toast Notification und geben so dem Benutzer einen weiteren visuellen Hinweis, dass eine Nachricht eingegangen ist (Listing 8).

Intent mainIntent = new Intent(Intent.ACTION_MAIN);
mainIntent.setClass(context, NPNActivity.class);
mainIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
mainIntent.putExtras(intent.getExtras());
mainIntent.putExtra(NOTIFICATION_BOOT,true);

PendingIntent contentIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, "Jax", tickerText, contentIntent);
nm.notify(1, notification);
LayoutInflater inflater = LayoutInflater.from(context.getApplicationContext());
View layout = inflater.inflate(R.layout.notification, null);
ImageView image = (ImageView) layout.findViewById(R.id.image);
image.setImageResource(R.drawable.icon_small);
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText(tickerText);
Toast toast = new Toast(context.getApplicationContext());
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();Notification.FLAG_AUTO_CANCEL;

Aufmacherbild: Close up of human hand pushing media icon von Shutterstock / Urheberrecht: Sergey Nivens

[ header = Testen der App ]

Testen der App

Nachdem wir jetzt den Plug-in-Code ein wenig durchleuchtet haben, wird es Zeit eine Testnachricht an unser Endgerät zu schicken. Hierfür ist es notwendig, dass wir unser Endgerät bei dem GCM-Dienst anmelden. Die App wird gestartet, und der Link Aktiviere Push Notifikationen geklickt. Das Ergebnis dieser Aktion zeigt Abbildung 3.

Abb. 3: Screenshot nach einer korrekten Registrierung

Die registration_id ist auch im Android Log zu finden: einfach nach fetched registrationId das Logfile durchsuchen.

Fehler aufgetreten – Was nun?

1. Hat das Endgerät eine funktionsfähige Internetverbindung? 2. Wurde die PROJECT_ID in der NPNPlugin.java korrekt gesetzt? Bitte in jedem Fall das Android Log lesen.

Da wir Entwickler sowenig Code wie möglich schreiben, werden wir unsere Testnachricht mittels curl verschicken. Sollte dem Leser curl nicht zur Verfügung stehen, so empfehle ich die Verwendung eines REST-Clients. Der curl-Aufruf sollte wie folgt aussehen:

curl --header "Authorization: key=_API_KEY_" –data "registration_id=_REGISTRAION_ID_&data.msg=Und täglich grüsst das Murmeltier"

In dieser Zeile benötigen wir zwei Parameter, den API_KEY sowie die REGISTRATION_ID. Die curl-eigenen Parameter sollten selbsterklärend sein. Abbildung 4 und 5 zeigen zwei Screenshots meines Smartphones. Abbildung 4 zeigt eine erfolgreich empfangene Nachricht, für den Fall, dass die App im Vordergrund läuft. Auf Abbildung 5 sieht man den „Toast“ sowie das Icon auf der „Status Bar“, wenn sich die App im Hintergrund befindet bzw. ausgeschaltet ist.

Abb. 4: Screenshot nach erhaltener Nachricht mit aktiver App

Abb. 5: Screenshot mit Toast bei inaktiver App

Ich hoffe, dass der Artikel dem Leser einen Einblick in die Plug-in-Entwicklung für das PhoneGap-Framework gegeben hat. Gerade weil das Beispiel ein wenig anspruchsvoller ist als vergleichbare Tutorials im Netz, sollte die Entwicklung eigener Plug-ins von jetzt an relativ leicht fallen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -