Die Android-Uhren sind gereift

Seite 3: Auf den Spuren alter Meister

Inhaltsverzeichnis

Bei Uhren bezeichnet der Begriff Komplikationen die Elemente, die nicht der eigentlichen Zeitanzeige dienen: In klassischen Armbanduhren sind den gebotenen Features aus Platzgründen enge Grenzen gesetzt.

Im Handcomputerbereich ist das Problem komplizierter: Zum einen ist auf einer Smartwatch der Anzeigeplatz ebenfalls eng beschränkt. Zum anderen führt das Anbieten einer Bitmap-Zeichenfläche am Homescreen zu Ärger: Microsoft implementierte so den klassischen Homescreen von Windows Mobile, der in der Theorie (und bei sorgfältiger Programmierung) perfekt funktionierte. Leider hatte Microsoft die Rechnung ohne diejenigen Nutzer gemacht, die schlecht programmierte Plug-Ins massenhaft aktivierten. Diese gingen zu Lasten der Rechenleistung und des Arbeitsspeichers, sodass es am Ende Kritiken über kurze Laufzeit und schlechte Reaktivität hagelte.

Auch in Android Wear erfolgt der Datenaustausch über einen Zwischenhändler.

(Bild: Google)

Aktuell unterstützt Google sechs unterschiedliche Datentypen, die in der folgenden Tabelle kurz zusammengefasst sind. Entwickler müssen einen passenden Datentyp auszuwählen und ihn über einen Service bereitstellen. Die folgenden Schritte setzen auf RANGED_VALUE. Android Wear bietet bei Symbolen neben der normalen Grafik zusätzlich eine farbreduzierte (Burn-in Protection) Version an, ein Symbol, das die teilweise einbrenngefährdeten OLED-Bildschirme (Stichwort SGS II und ebay-App) weniger stark belasten sollte.

Typ Erforderliche Daten Optionale Daten
SHORT_TEXT Kurztext Symbol
Farbreduziertes Symbol
Kurztitel
ICON Symbol Farbreduziertes Symbol
RANGED_VALUE Wert
Maximum
Minimum
Symbol
Farbreduziertes Symbol
Kurztext
Kurztitel
LONG_TEXT Langer Text Langtitel
Symbole
Bitmap
SMALL_IMAGE Bitmap
LARGE_IMAGE Bitmap

Zunächst müssen Entwickler die Manifestdatei um die Deklaration eines ComplicationProviderService erweitern, was mit folgendem Snippet geschieht, das die Berechtigung BIND_COMPLICATION_PROVIDER deklariert:

<application . . .>
<service
android:name=".HeiseRangedClass"
android:label="HeiseRangedAnzeiger"
android:icon="@mipmap/ic_launcher"
android:permission=
"com.google.android.wearable.permission.
BIND_COMPLICATION_PROVIDER">
<intent-filter>
<action android:name=
"android.support.wearable.complications.
ACTION_COMPLICATION_UPDATE_REQUEST"/>
</intent-filter>
//Metadata hier
</service>

Aktualisierungen können entweder automatisch oder auf Anfrage erfolgen. Für Letzteres muss UPDATE_PERIOD_SECONDS den Wert Null haben. Folgender Code aktualisiert die Darstellung alle zehn Sekunden.

<meta-data
android:name=
"android.support.wearable.complications.
UPDATE_PERIOD_SECONDS"
android:value="10" />

Die Metadata-Deklarationen gehören in den Service und dürfen nicht auf Ebene des Gesamtsystems liegen. Auch die Festlegung der unterstützten Datentypen erfolgt im Manifest – interessanterweise können Komplikations-Provider auch mehrere Werte unterstützen:

<meta-data
android:name=
"android.support.wearable.complications.SUPPORTED_TYPES"
android:value="RANGED_VALUE,SHORT_TEXT,ICON"/>

Für das Beispiel genügt es, value nur einen Wert zuzuweisen:

<meta-data
android:name=
"android.support.wearable.complications.SUPPORTED_TYPES"
android:value="RANGED_VALUE"/>


Beim Kompilieren der Komplikationen kommt es immer wieder zu Meldungen, die auf fehlende Namespaces hinweisen. Die Ursache dafür ist, dass die Deklaration der Klasse in build.gradle veraltet ist. Abhilfe schafft das Verwenden von + statt einer spezifischen Version:

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:+'


Nun fehlt noch die in der Manifest-Datei angemeldete Serviceklasse, deren Deklaration das Vorhandensein einer Methode voraussetzt:

public class HeiseRangedClass
extends ComplicationProviderService {
@Override
public void
onComplicationUpdate(int complicationId,
int dataType,
ComplicationManager complicationManager) {
}

Von besonderem Interesse sind dabei zwei Felder: complicationID liefert eine Korrelations-ID, während dataType die Art der Anfrage gemäß der oben gezeigten Tabelle ermöglicht. Die eigentliche Methode sieht folgendermaßen aus:

public void
onComplicationUpdate(int complicationId,
int dataType,
ComplicationManager complicationManager) {
ComplicationData complicationData = null;
if (dataType == ComplicationData.TYPE_RANGED_VALUE){

Im ersten Schritt erfolgt eine Überprüfung der angelieferten Anfrage: Der ComplicationData-Builder verlangt bereits bei der Initialisierung der neuen Klasseninstanz Informationen darüber, welche Art von Anfrage zu bedienen ist. Das eigentliche Bevölkern der Anfrage erfolgt über die von JavaScript bekannte Vorgehensweise der verketteten Aufrufe von set-Methoden, die jeweils den aktuellen Zustand der Klasse zurückliefern und sich somit als a().b().c() verknüpfen lassen:

complicationData =
new ComplicationData.Builder(
ComplicationData.TYPE_RANGED_VALUE)
.setValue(22)
.setMinValue(0)
.setMaxValue(100)
.setShortText(ComplicationText.plainText("Hallo Welt!"))
.build();}

Da das Aktualisieren der Bildschirminhalte Energie verbraucht, können Complication-Services das Betriebssystem auch dazu auffordern, keine Aktualisierung durchzuführen:

if(complicationData!=null)complicationManager.updateComplicationData(complicationId, complicationData);
else complicationManager.noUpdateRequired(complicationId);


Google spendiert eine Gruppe zusätzlicher Funktionen, deren Implementierung weitere Informationen über das Verhalten beziehungsweise den Lebenszyklus des Gesamtsystems liefern. Der folgende Code zeigt die beiden Methoden, deren Aufruf beim Aktivieren und Deaktivieren der Komplikation erfolgt:

@Override
public void
onComplicationActivated(int complicationId,
int dataType,
ComplicationManager complicationManager) {
Log.d("Heise",
"onComplicationActivated(): " + complicationId);
super.onComplicationActivated(complicationId,
dataType,
complicationManager);
}
@Overridepublic void
onComplicationDeactivated(int complicationId) {
Log.d("Heise",
"onComplicationDeactivated(): " + complicationId);
super.onComplicationDeactivated(complicationId);
}

Nach dem Laden der Applikation in den Emulator öffnet langes Klicken im leeren Bereich der Uhrenanzeige das Kontextemenü. Entwickler können die Option data und dort das Beispielprogramm auswählen. Anschließend sollte die Komplikation wie in der folgenden Abbildung erscheinen.

Alles funktioniert problemlos.