Buddy-Tracker (Technik)

Target API ist im Moment die 25 (Nougat 7.1) und die Minimum API ist 9. Anf├Ąnglich habe ich bis Level 8 unterst├╝tzt (da ich noch so ein uraltes Testger├Ąt habe), aber die Ger├Ątebasis ist jetzt zu klein geworden und die n├Âtigen Klimmz├╝ge f├╝r API-Levels unter 9 werden zu gro├č. Unterhalb von API 9 wird auch die Unterst├╝tzung seitens Google recht sp├Ąrlich.

Nach einer umfangreichen ├ťberarbeitung des Codes habe ich den Buddy-Tracker nun mit einer 1.x-Version geadelt. ;-) Hier hat fast der komplette Code eine Frischzellenkur bekommen. Dabei habe ich evaluiert wie sich die Location-Services der Google Play Services verhalten. Da die internen Aufrufe dieser Library hochgradig asynchron sind ist das auch ein gutes Feld um Erfahrungen mit reaktiver Programmierung zu sammeln. Ich habe also mit RxJava einen reaktiven Wrapper um diese Library geschrieben. Funktioniert so weit ganz gut, aber leider mu├čte ich das Ganze wieder deaktivieren, denn die Qualit├Ąt der gelieferten Positionsdaten ist grottenschlecht. Man kann auch nicht nur GPS als Quelle der Positionsdaten ausw├Ąhlen, sondern mu├č immer auch den Netzwerk-Provider mitnutzen. Einige sehen das ja kritisch, weil dabei auch immer die eigenen Bewegungsdaten an Google gesendet werden. Aber dessenungeachtet ist die KI, die Google da verwendet offenbar noch verbesserungsf├Ąhig, denn auch wenn man Positionen mit hoher Genauigkeit anfordert, liefern die Location Services teilweise Positionen mit einer Genauigkeit von 1000m zur├╝ck - und das auch weit ab vom jeweiligen Track. Da ich das Ganze - Dependency-Injection sei Dank - konfigurierbar machen konnte, konnte ich jeweils den Gegencheck machen und dieselbe Strecke statt mit den Google Services mit den Android-Location Services abgehen. Hier sind die Positionsdaten erheblich besser und die Genauigkeit war (auf derselben Strecke!) immer besser als 20m - was man bei GPS unter freiem Himmel ja auch erwarten kann.

Inzwischen hatte es Google gefallen, die Aufrufbedingungen f├╝r die SyncAdapter zu ├Ąndern. Damit hatte ich die Synchronisation der eMails realisiert. Nun war die minimale Synchronisationsperiode von 10 Minuten auf eine Stunde hoch gesetzt worden. Viel zu hoch f├╝r meine Bed├╝rfnisse. Also mu├čte eine andere L├Âsung gefunden werden. Ich hab dann den Firebase-JobScheduler eingesetzt der zwar noch Beta ist, aber den GCM-Networkmanager abl├Âsen soll. Da im Moment alles funktioniert …

Beim Refactoring hat es sich angeboten, die bestehenden Unit- und Instrumentation-Tests zu erweitern. Gibt einem einfach viel mehr Sicherheit wenn man am Code rum schraubt. Der Buddy-Tracker ist inzwischen so komplex geworden, da├č man nicht mehr immer alles gleichzeitig im Blick haben kann. Ist schon spannend, beobachten zu k├Ânnen wie sich der eigene Blickwinkel auf den Code ├╝ber die Zeit langsam ver├Ąndert hat.

Mir gefiel die ganze Zeit eigentlich noch nicht, wie die Daten geteilt werden konnten. Wegen den flexiblen ├ťbertragungsm├Âglichkeiten konnte ich keinen Standard-Share Prozess nehmen, sondern mu├čte etwas eigenes bauen. Das hatte aber zur Folge, da├č die ganzen Kontakte immer erst einmal im Buddy-Tracker eingetragen werden mussten. Schnell mal was an einen bestehenden Kontakt senden war so nicht m├Âglich. ich habe dann den ShareActionProvider eingesetzt um die Information auch ├╝ber andere bereits auf dem Smartphone installierte Apps teilen zu k├Ânnen. Aber die Art und Weise wie man den Share-Intent kontinuierlich aktuell halten mu├čte ohne da├č sichergestellt war, da├č die Share-Option ├╝berhaupt genutzt wurde, schmeckte mir nicht. Die L├Âsung ist DirectShare. Hier kann der Benutzer direkt mit h├Ąufig genutzten Kontakten teilen oder andere externe Apps nutzen. Eine dieser Apps ist dabei dann auch wieder der Buddy-Tracker, der dann den alten Dialog zur Gruppenauswahl der Buddies bereit stellt. Man hat jetzt also das Beste aus beiden Welten. Auf Ger├Ąten die noch nicht Lollipop haben funktioniert das auch, nur f├Ąllt die Option weg, direkt an Kontakte zu senden. Hier mu├č man die Auswahl dann jeweils in der gew├Ąhlten App treffen.

Und es gibt noch etwas Eye-Candy: F├╝r Buddies und Orte k├Ânnen Bilder angegeben werden, die dann bei den Markern auf der Karte verwendet werden. Nat├╝rlich wollte ich dabei die Default-Icons als Vector-Drawables haben - hat ein bi├čchen gedauert, bis ich das gel├Âst hatte. Hier ist der Lint-Checker f├╝r den Code nicht ganz vollst├Ąndig gewesen. Der Code war frei von entsprechenden Warnungen, ist aber auf alten Ger├Ąten gecrasht weil entsprechende Systemfunktionen fehlten …

Apropos Bilder:

Auf den Ger├Ąten sind ja meist viele Fotos gespeichert und die Ger├Ąte erlauben in zu den Bildern auch die Ortsinformationen zu speichern. Da liegt es nahe, diese Informationen auch zu nutzen und die Bilder dann in der Karte anzuzeigen. Daraus ist das Modul Buddy-Tracker Pictures entstanden.

Dabei entsteht schnell das Problem, da├č zu viele Eintr├Ąge auf die Karte gezeichnet werden m├╝ssen. Passenderweise bietet Google f├╝r seine Kartenansicht ein Clustering-Modul an, da├č diese Eintr├Ąge zu Gruppen zusammenfassen kann um so mehr ├ťbersicht auf der Karte zu haben. Funktioniert recht performant.

Allerdings hab ich im Moment noch das Problem, da├č das Zooming dann noch nicht reibungsfrei funktioniert –> Daher noch in der Beta-Phase.

Mit der Version 0.5. hatte ich das User-Interface gewechselt. Anfangs habe ich das Tab-Layout verwendet. Es machte f├╝r mich Sinn, da es nur eine ├╝bersichtliche Anzahl von Men├╝optionen ab. So konnte man schnell und bequem zwischen den Programmteilen wechseln. Mit der Zeit kamen weitere Fenster hinzu, die nicht prominent genug f├╝r einen eigenen Tab waren. Also hatte ich sie im Kontext-Men├╝ geparkt. Zusammen mit den jeweiligen Eintr├Ągen f├╝r den Kontext des gerade aktiven Tabs wurde das jetzt aber zu un├╝bersichtlich. Mit dem Drawer-Layout sind die verschiedenen Arten von Men├╝eintr├Ągen jetzt wieder sauber getrennt. Au├čerdem hab ich etwas Arbeit in das Layout investiert. Ein paar Paddings, etwas Farbe … macht schon einiges aus ;-)

Das Tracking kann ├╝ber den Activity-Recognition-Service von Google gesteuert werden. So kann Energie gespart werden, wenn das Ger├Ąt ruht.

Die App kann ├╝ber diverse Broadcast-Intents ferngesteuert werden. So kann ein individuelles Verhalten mit Apps wie Tasker, Llama oder Automate hinzu gef├╝gt werden (Tracking nur wenn nicht mit bestimmten WLANs verbunden, nur zu bestimmten Zeiten, …) So k├Ânnte man vielleicht so etwas wie einen automatischen T├Ątigkeitsnachweis oder ein Fahrtenbuch realisieren.

Zu vielen Themen gibt es eine kontextsensitive Hilfe, die eine HTML-Datei aus der App anzeigt. F├╝r die Hilfe ist also keine Internet-Verbindung n├Âtig.

Zu Debugging-Zwecken k├Ânnen die gerade erkannten Aktivit├Ąten auch per Sprachausgabe ausgegeben werden, so da├č man nicht dauernd auf das Ger├Ąt starren mu├č um zu wissen ob er gerade trackt oder nicht.

Kommunikationsmethoden Der Buddy-Tracker kann ├╝ber verschiedene Methoden Kontakt zu den Buddies aufnehmen.

Warum mehr als eine Methode? Jede der Methoden hat ihre Besonderheiten. SMS ist sehr einfach, aber die Kommunikationsmenge ist sehr begrenzt und es kostet Geld. Einen eMail-Account hat jeder, aber nicht jeder will sich seine Inbox mit diesen automatischen Mails zum├╝llen oder einen separaten Mailaccount daf├╝r anlegen. Shared Folder ben├Âtigt Zusatzsoftware, die auch erstmal beherrscht werden will … Au├čerdem kann ich so mit den Methoden experimentieren und Erfahrungen sammeln, wie praktikabel sie jeweils sind und wo ihre St├Ąrken und Schw├Ąchen liegen.

eMail F├╝r die Klartext-Kommunikation mit Buddies, die den Buddy-Tracker nicht installiert haben, nutze ich den entsprechenden Android-Intent, der die jeweilige Default-eMail App startet und den Mailbody mit den entsprechenden Informationen f├╝llt. Dies erfordert keine besonderen Berechtigungen f├╝r die App und der Benutzer kann seine gewohnten Kontaktdaten verwenden.

F├╝r die regelm├Ą├čige automatische Kommunikation von Buddy-Tracker zu Buddy-Tracker ist diese Methode aber ungeeignet. Hier mu├č ich alle Mailparameter inclusive des Versands und des Abholens von Mails steuern k├Ânnen. Dies realisiere ich mit den Open-Source Mail-Funktionen aus der JavaX-Library. Die entsprechenden Funktionen werden dann ├╝ber einen SyncAdapter aufgerufen. Um diesen benutzen zu k├Ânnen mu├č ich nat├╝rlich auch die passenden Account-Services erzeugen und bedienen.

Hier steht ein Update zum Firebase-Network-Manager an, der nochmal erweiterte Konfigurationsoptionen hat. So z.B. das Versenden von Mails abh├Ąngig von der jeweils aktuellen Verbindungsart. K├Ânnte ich nat├╝rlich aus den System-Intents nachbilden, aber wenn es daf├╝r vorgefertigte Libraries gibt …

Shared Folder Die Idee dahinter ist simpel: Es gibt Software mit der man einen lokalen Ordner und dessen Unterordner ├╝bers Internet mit Freunden teilen kann. Die Kommunikation ist dabei verschl├╝sselt und nur die gew├╝nschten Freunde haben auch Zugriff. Also legt der Buddy-Tracker f├╝r sich in diesem Ordner einen Unterordner an, in dem er die zu teilenden Daten dann in Form einzelner kleiner Dateien, die die Nachrichtenart und den Zeitstempel im Dateinamen und die Payload als Dateiinhalt haben, ablegt. Teilt man diesen Ordner dann mit Freunden, so bekommt man dadurch die Unterordner der Freunde auf sein Ger├Ąt synchronisiert. Synchronisiert dann die Sharing-Software neue Dateien in diesen Ordner, so bekommt das der Buddy-Tracker ├╝ber File-Watcher, die in einem Hintergrund-Service gestartet werden, mit und kann die Daten enstprechend verarbeiten.

Allerdings ist man auf die Synchronisationseigenschaften dieser Software angewiesen …

SMS Eigentlch nur der Vollst├Ąndigkeit halber implementiert, denn alleine der Kostenfaktor schr├Ąnkt den Einsatz wohl sehr ein. Aber da die Funktion recht einfach umzusetzen ist …

Location-Services Als ich mit dem Buddy-Tracker angefangen habe, gab es zur Positionsbestimmung nur die Location-Services aus dem Paket android.location. Inzwischen propagiert Google seine eigenen Location-Services aus den Google-Play Services. Ich habe sie einem ersten Test unterzogen und f├╝r im Moment ungeeignet befunden.

Neben dem asynchronen Aufruf der Funktionen war die Qualit├Ąt der Positionsdaten nicht ausreichend. Auch wenn man Positionen mit hoher Pr├Ązision anfordert liefert dieser Dienst gelegentlich Ergebnisse mit einer Genauigkeit von gr├Â├čer 500 Metern zur├╝ck (und zwar reproduzierbar in einigen Gebieten). Und das im zwar im Stadtgebiet aber unter freiem Himmel an Orten an denen die alten Android-Location Services problemlos genaue GPS-Daten liefern.

Abgesehen davon arbeiten die Google-Location Services ├╝berwiegend asynchron - man mu├č sich erst mit dem Google-API-Client mit der Google-API verbinden und kann erst dann seine eigentlichen Abfragen starten. F├╝r die periodische Positionsabfrage im Hintergrund ist das O.K. Aber um zwischendurch mal einzelne Werte abzufragen - insbesondere bei Start der App, sind doch einige Klimmz├╝ge n├Âtig. Will ich aber nur feststellen, ob der User gerade die Location-Services deaktiviert hat um z.B. auf der Karte den Button f├╝r die eigenen Position ausblenden zu k├Ânnen, kann ich nicht einfach das System fragen, sondern mu├č eine asynchrone Anfrage starten und das Ergebnis dann in einem Callback verarbeiten.

Zum Test habe ich die API-Aufrufe in einer Klasse gekapselt. In dieser Klasse gibt es eine weitere abstrakte Klasse, die meine interne private API f├╝r den Zugriff auf die Location-Services abstrahiert habe. Von dieser abstrakten Klasse sind dann zwei Klassen (wieder private innere Klassen) abgeleitet, die die jeweiligen APIs konkret umsetzen. So kann ich sogar on the fly die verwendeten Location-Services umschalten und die Ergebnisse miteinander vergleichen.

Des weiteren zeichne ich bei Ger├Ąten die einen Luftdrucksensor eingebaut haben, auch den aktuellen Luftdruck bei einer Positionsmessung auf. Aktuell wird das noch nicht weiter ausgewertet, verspricht aber bessere Ergebnisse bei der Bestimmung der H├Âhenunterschiede auf dem Track da die Luftdruckmessung genauer als die Werte vom GPS sein sollten.

Bemerkungen zu den Programmteilen Karte Die Kartendarstellung verwendet im Moment das Map-Fragment von Google-Maps. Auf dieser Karte werden die aktuellen Positionsdaten der Buddies (ggf. mit ihrer Historie) und die aktiven Orte angezeigt.

Die angezeigten Daten k├Ânnen gefiltert werden. Im Moment habe ich zeitbasierte Filter implementiert. Einmal die Anzeige zwischen Start- und Enddatum und alternativ ein w├Ąhlbarer Zeitraum von jetzt in die Vergangenheit (die letzten x Stunden …) Dabei kann dann auch noch die Zeitzone eingestellt werden, in der die Zeitangaben angezeigt werden. Praktisch, wenn die beobachteten Positionen weit vom eigenen Standort entfernt sein sollten und man jeweils die dortige lokale Zeit wissen m├Âchte.

Die jeweils angezeigten Track-Daten k├Ânnen hier im GPX-Format eportiert werden oder neue Daten aus GPX-Dateien eingelesen werden. Dabei habe ich (konform zum GPX-Standard) Erweiterungen implementiert um meine zus├Ątzlichen Attribute zu den Positionsdaten auch mit abspeichern und wieder einlesen zu k├Ânnen.

Nette Gimmicks sind:

Kurzer Klick auf einen angezeigten Marker ├Âffnet ein Fenster mit Zusatzinformationen Klick auf dieses Fenster ├Âffnet bei Orts-Markern das entsprechende Fragment mit dem man diese Daten dann bearbeiten kann Bei Positionsmarkern geht es da nicht weiter. Daf├╝r wird aber der zugeh├Ârige Track gekennzeichnet indem er mit einer dickeren Strichst├Ąrke gezeichnet wird. Hat man auch noch die Option aktiviert die Genauigkeit der Ortsdaten anzuzeigen, wird f├╝r jeden Datenpunkt des Tracks ein Marker erzeugt in dessen Info-Window man auf Klick die Details zu diesem Trackpoint bekommt. Au├čerdem wird die Genauigkeit der Position mit einem entsprechenden Kreis um die Position markiert. Langer Klick auf einen leeren Bereich der Karte erzeugt einen neuen Marker (mit dem festen Namen “Wpt 1”, der dann aber ge├Ąndert werden kann (s.o.)) Langer Klick auf einen Ortsmarker bringt diesen in den Drag-and-Drop Mode wo man ihn dann beliebig auf der Karte verschieben kann.

Eine einfache Navigation zum Zielort ohne Stra├čenrouting, also geeignet f├╝r die offene See oder wenn man in der Natur ohne Stra├če querfeldein wandert. Aus dem eigenen Standort und den Zielkoordinaten wird Entferung und Richtung zum Ziel bestimmt. Dies wird mit einem selbst geschriebenen View, der ein HSI-Instrument (Horizontal Situation Indicator) aus der Fliegerei nachbildet visualisiert und zus├Ątzlich werden die Zahlenwerte diverser Eigenschaften noch aufgelistet.

Aufgrund der hohen Update-Frequenz darf ich mir hier keine Memory-Leaks erlauben und mu├č auf die Performance achten. Ist mir gelugen, denn dieses Fragment l├Ąuft auch auf Uralt-Ger├Ąten (damals noch mit Android 2.2)

Orte, Buddies, Teilen

Keine gro├čen Besonderheiten. Es werden Listviews mit Mehrfachauswahl und Action-Men├╝s eingesetzt.

ToDo

Android Wear

Bei aktivem Navigationsmodus Informationen zum aktuellen Ziel (Richtung, Entfernung, TTG, ETA) und zum aktiellen eigenen Bewegungszustand (SOG, COG) anzeigen. Anzeige auch im Ambient-Modus, allerdings mit reduziertem Detailreichtum.

Android N

Anpassen an die diversen Änderungen in der neuen Version von Android.