diff --git a/.hgignore b/.hgignore index 7220e4cb0..3bf9706a8 100644 --- a/.hgignore +++ b/.hgignore @@ -2,5 +2,6 @@ syntax:glob */bin */gen/** +*/proguard/** .metadata MyTracksTest/annotations.log diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index 5f52f7262..6f3b16692 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -119,6 +119,9 @@ limitations under the License. + + + @@ -172,6 +175,7 @@ limitations under the License. - Off + Aus 1 min 2 min 5 min @@ -70,8 +70,8 @@ limitations under the License. - batterie sparend (adaptiv) - hohe genauigkeit (adaptiv) + batteriesparend (adaptiv) + hohe Genauigkeit (adaptiv) maximal (empfohlen) 2 s 3 s @@ -133,12 +133,12 @@ limitations under the License. Nicht zerteilen - 100 meilen - 50 meilen - 25 meilen - 10 meilen - 5 meilen - 1 meile + 100 Meilen + 50 Meilen + 25 Meilen + 10 Meilen + 5 Meilen + 1 Meile 60 min 30 min 15 min @@ -159,7 +159,7 @@ limitations under the License. Laufbahn Geländelauf Gehen - Schnelles Gehen + Gehen (Sport) Wandern Bergwandern Bergsteigen @@ -181,11 +181,11 @@ limitations under the License. Eissegeln Segeln - Kanadierfahren - Kajakfahren + Paddeln + Kajak fahren Kanuwandern Rudern - Motorbootfahren + Motorboot fahren Fähre Ferngesteuertes Boot Surfen @@ -229,13 +229,14 @@ limitations under the License. Bank Kneipe Grill + Imbiss Gebäude Bushaltestelle Straßenbahnhaltestelle Bahnhof S-Bahnhof Café - Lagerfeuer + Feuerstelle Campingplatz Zeltplatz Kirche @@ -278,6 +279,7 @@ limitations under the License. Tunnel Baum Wasserfall + Springbrunnen Wegpunkt diff --git a/MyTracks/res/values-de/strings.xml b/MyTracks/res/values-de/strings.xml index 1d53433ba..571094c20 100644 --- a/MyTracks/res/values-de/strings.xml +++ b/MyTracks/res/values-de/strings.xml @@ -1,422 +1,399 @@ - + + - - - - Track aufzeichnen - Aufzeichnung beenden - Satellitenansicht - Kartenansicht - Mein Aufenthaltsort - Wegpunkte - Alle Tracks löschen - Einstellungen - Höhe - Bitte wählen Sie einen Track zur Ansicht aus - Aktueller Track - Letztes Segment - Sensor-Status - Gesamtstatistik - - - Es wurde kein \'Location Manager\' auf diesem Gerät gefunden. - Konnte keinen GPS-Empfänger auf diesem Gerät finden. - Beim Senden Ihres Tracks an Google My Maps ist ein Fehler aufgetreten. Bitte versuchen Sie es später nochmal. - Beim Senden Ihres Tracks an Google Docs ist ein Fehler aufgetreten. Bitte versuchen Sie es später nochmal. - Aufzeichnen fehlgeschlagen. Bitte versuchen Sie es nochmal. - Anhalten nicht möglich. Bitte versuchen Sie es nochmal. - Datei nicht lesbar. - Datei ist nicht im GPX-Format. - Ein Fehler ist aufgetreten. Bitte versuchen Sie es nochmal. - Track existiert nicht. - Die Datei ist zu groß (max. 500 KB erlaubt). Operation abgebrochen. - Der Wegpunkt kann nicht einfügt werden. Keine Positionsbestimmung möglich. - - - Warten auf ein besseres GPS-Signal. Bitte stellen Sie sicher, dass der Himmel sichtbar ist. - Ein neuer Track wird aufgezeichnet. - Die Aufzeichnung wurde beendet. - Eine neue Karte wurde auf maps.google.com erstellt. - Ihr Track wurde auf maps.google.com gespeichert. - Ihr Track wurde in einer neuen Tabelle bei docs.google.com gespeichert. - Ihr Track wurde an docs.google.com gesendet. - Manchmal dauert es ein paar Minuten, bis neue Tracks bei maps.google.com erscheinen. - Berühren Sie hier um zu den Sicherheits- und Positionseinstellungen zu gelangen. Aktiviere \'GPS-Satelliten\'. Drücken Sie dann den Zurück-Knopf um zurückzukehren. - Anmerkung: Eine ungefähre Position wird angezeigt, bestimmt mittels Funktürmen und WiFi-Netzwerken. - Ihre Position kann derzeit nicht bestimmt werden. Aktivieren Sie mehr Positionsquellen (siehe Sicherheits- und Positionseinstellungen) und stellen Sie sicher, dass der Himmel sichtbar ist. - Ein Wegpunkt wurde eingefügt. - Ein Statistikwegpunkt wurde eingefügt. - - - Bitte warten… - Ihr GPS-Track wird an Google gesendet. - Lese Track… - Bereite Track vor… - Anmelden bei Google Maps… - Anmelden bei Google Docs… - Track an Google Docs senden… - Track an Google Maps senden… - Neue \'Meine Karte\' erstellen… - Importiere GPS-Track. Das kann einen Moment dauern. - Schreibe Datei auf SD-Karte. Das kann einen Moment dauern. - Aktualisiere Trackstatistik… - - - Sind Sie wirklich sicher? - Alle Tracks und Wegpunkte werden endgültig von diesem Gerät gelöscht. - Dieser Track und seine Wegpunkte werden endgültig von diesem Gerät gelöscht. - Dieser Wegpunkt wird endgültig von diesem Gerät gelöscht. - - - Erstellt mit My Tracks auf Android. - Erstellt mit <a href=\'http://mytracks.appspot.com\'>My Tracks</a> auf Android. - Neue Karte - - - Punkte - Aufgezeichnet am - "Aktivität" - "Art des Wegpunktes" - "(Teil %d)" - - - Ihr Track wird aufgezeichnet… - Track %d - - - Nein - Ja - OK - Fehler - Erfolgreich - Wegpunkt - - - (Anfang) - (Ende) - - - Anzeigen - An eine Google MyMaps-Karte senden - An eine neue Google MyMaps-Karte senden - An Google Docs senden - An Google senden… - Vom Gerät löschen… - Bearbeiten - Auf SD-Karte speichern… - Als GPX-Datei exportieren - Als KML-Datei exportieren - Als CSV-Datei exportieren - Als TCX-Datei exportieren - An Google MyMaps senden - Alle Tracks löschen - - Alle Tracks exportieren - Export abgeschlossen - Eine Track auswählen. Lange berühren für mehr Optionen. - Alle Tracks: - Dieser Track - Mit Freunden teilen… - Ein Link zur Google MyMaps-Karte - Als GPX-Datei - Als KML-Datei - Als CSV-Datei - Als TCX-Datei - Karte zurücksetzen - - - Alle Wegpunkte löschen - Dieser Wegpunkt - Löschen - Details bearbeiten - Auf der Karte zeigen - Wegpunkt einfügen - Statistik einfügen - Wegpunkt auswählen. Lange berühren für mehr Optionen. - - - Trackdetails - Name - Beschreibung - Aktivität (z. B. Wandern, Fahrradfahren, Laufen) - - - Wegpunktdetails - Wegpunktname - Wegpunktbeschreibung - Art des Wegpunktes (z. B. Gebäude, Schlagloch, Sehenswürdigkeit) - - - Einstellungen für My Tracks - Anzeige - Aufzeichnen - Mit Freunden Teilen - - Mit Ihrer Version von Android nicht verfügbar - - Dauer zwischen Ansagen: - Kürzeste Zeit zwichen zwei Ansagen - - Kleinste Entfernung zwischen Punkten: - Kleinste Entfernung zwischen zwei Positionsaufzeichnungen - - Neues Segment wenn Punkte weiter entfernt als: - Der Track wird in zwei Segmente geteilt wenn zwei folgende Punkte weiter entfernt sind als hier angegeben. - - Aufzeichnungsfrequenz: - Kürzeste Zeit zwischen zwei Positionsaufzeichnungen. - - Aufzeichnung pausieren wenn Genauigkeit kleiner als: - Die Aufzeichnung wird kurzfristig angehalten, wenn Ihre Position nicht genauer als hier angegeben ermittelt werden kann. - - Metrischen Einheiten verwenden - Alle Entfernungen werden in metrischen Einheiten angezeigt. - Alle Entfernungen werden in britischen Längeneinheiten angezeigt. - - Karten auf maps.google.com veröffentlichen - Hochgeladene Karten auf maps.google.com sind für alle sichtbar. - Hochgeladene Karten auf maps.google.com werden nicht öffentlich sichtbar aufgelistet. - - Geschwindigkeitsanzeige: - Geschwindigkeit wird angezeigt. - Gangart wird angezeigt. - - Nur URL als Einladung zum Teilen senden - Beim teilen mit Freunden wird nur die Karten URL gesendet. - Beim teilen mit Freunden wird der GPS Track selbst gesendet. - - Zerteilungs Frequenz - Längste Dauer oder Entfernung nach der automatisch ein Marker eingefügt wird. - - Maximale Dauer für Fortsetzung nach Neustart - Maximale Zeit in Minuten nach welcher eine Aufzeichnung nach Neustart des Geräts fortgesetzt wird - - Datum und Zeit als Trackname - Neue Tracks erhalten Startdatum und -zeit als Tracknamen - Neue Tracks werden durch fortlaufende Nummerierung benannt - - Tempo - - Nur auf Android 2.0 und höher verfügbar - Art des Sensors - Die Art des Sensors, der angeschlossen wird - Sensor ID - Kennung, die an Sensoren gesendet wird (Zahl) - Die ID darf nur aus Ziffern bestehen - Bluetooth-Sensor - Der Bluethooth-Sensor, der angeschlossen wird - Modulare Statistik-Anzeige - Die Statistiken werden auf mehreren Bildschirmseiten angezeigt - Alle Statistiken auf einem Bildschirm anzeigen - - - Datensicherung (BETA) - Datensicherung auf SD-Karte - Daten jetzt sichern - Speichert eine Sicherungskopie aller Tracks und Einstellungen auf der SD-Karte - Sicherung wiederherstellen - Stellt Tracks und Einstellungen von einer Sicherungskopie auf der SD-Karte wieder her - Während einer Track-Aufzeichnung können keine Datensicherungen erstellt werden - - Sind Sie sicher? Das Wiederherstellen einer Sicherungskopie überschreibt ihre aktuellen Tracks und Einstellungen. - Wählen Sie die Sicherungskopie, die Sie wiederherstellen möchten. - Keine Sicherungskopien gefunden - Erstelle Sicherungskopie von Tracks und Einstellungen. Das kann eine Weile dauern... - Lese Tracks und Einstellungen aus der Sicherungskopie Das kann eine Weile dauern... - - - GPS-Satelliten - - - Wählen Sie eine Karte von Ihrem Konto aus: - Neue Karte erstellen… - An Google My Maps senden - Karten werden geladen… - Ihre Karten konnten im Augenblick nicht geladen werden - Diese Karte auswählen - Kartendetails bearbeiten - Karte mit Freunden teilen - Track mit Freunden teilen - Karte vom Konto löschen - Kartentitel: - Beschreibung: - Diese Karte und Ihren Inhalt in Suchergebnissen sichtbar machen. - Diese Karte und Ihren Inhalt nicht in Suchergebnissen sichtbar machen. - Diese Karte und Ihr Inhalt sind in Suchergebnissen sichtbar. - Weder diese Karte noch Ihr Inhalt sind in Suchergebnissen sichtbar, allerdings können Sie trotzdem von anderen durch einen direkten Link angezeigt werden. - Schließen - Bearbeiten - Abbrechen - Sichern - - - Ich möchte eine Karte mit Dir teilen +--> + + + + Track aufzeichnen + Aufzeichnung anhalten + Satellitenansicht + Kartenansicht + Mein Aufenthaltsort + Wegpunkte + Alle Tracks löschen + Einstellungen + Höhe + Bitte wähle einen Track zur Ansicht aus + Aktueller Track + Letztes Segment + Sensorstatus + Aggregierte Statistiken + + + Es wurde kein Dienst zur Positionsermittlung auf diesem Gerät gefunden. + Es wurde kein GPS-Empfänger auf diesem Gerät gefunden. + Beim Senden des Tracks an Google MyMaps ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. + Beim Senden des Tracks an Google Docs ist ein Fehler aufgetreten. Bitte versuche es später noch einmal. + Start der Aufzeichnung fehlgeschlagen. Bitte versuche es noch einmal. + Anhalten unmöglich. Bitte versuche es noch einmal. + Konnte Datei nicht lesen. + Datei ist nicht im GPX-Format. + Ein Fehler ist aufgetreten. Bitte versuche es noch einmal. + Track existiert nicht. + Die Datei ist zu groß (maximal 500 KB). Operation abgebrochen. + Der Wegpunkt kann nicht einfügt werden. Keine Positionsbestimmung möglich. + + + Warte auf ein besseres GPS-Signal. Bitte stelle sicher, dass der Himmel sichtbar ist. + Ein neuer Track wird aufgezeichnet. + Die Aufzeichnung wurde angehalten. + Eine neue Karte wurde auf maps.google.com erstellt. + Dein Track wurde auf maps.google.com gespeichert. + Dein Track wurde in einer neuen Tabelle bei docs.google.com gespeichert. + Dein Track wurde an docs.google.com gesendet. + Manchmal dauert es ein paar Minuten, bis neue Tracks bei maps.google.com erscheinen. + Hier berühren, um zu den Sicherheits- und Positionseinstellungen zu gelangen. Aktiviere \'GPS-Satelliten\'. Drücke dann den Zurück-Knopf, um hierher zurückzukehren. + Anmerkung: Eine ungefähre Position wird angezeigt, bestimmt mittels Mobilfunkstationen und WiFi-Netzwerken. + Die Position kann derzeit nicht bestimmt werden. Aktiviere mehr Positionsquellen (siehe Sicherheits- und Positionseinstellungen) und stelle sicher, dass der Himmel sichtbar ist. + Ein Wegpunkt wurde eingefügt. + Ein Statistikwegpunkt wurde eingefügt. + + + Bitte warten… + Dein Track wird an Google gesendet... + Lese Track... + Bereite Track vor... + Anmeldung bei Google Maps... + Anmeldung bei Google Docs... + Track wird an Google Docs gesendet... + Track wird an Google Maps gesendet... + Neue \'Meine Karte\' wird erstellt... + Importiere GPS-Track. Das kann ein wenig dauern. + Schreibe Datei auf SD-Karte. Das kann ein wenig dauern. + Aktualisiere Trackstatistik... + + + Bist Du Dir wirklich sicher? + Alle Tracks und Wegpunkte werden endgültig von diesem Gerät gelöscht. + Dieser Track und seine Wegpunkte werden endgültig von diesem Gerät gelöscht. + Dieser Wegpunkt wird endgültig von diesem Gerät gelöscht. + + + Erstellt von My Tracks für Android. + Erstellt von <a href=\'http://mytracks.appspot.com\'>My Tracks</a> für Android. + Neue Karte + + + Punkte + Aufgezeichnet am + Aktivität + Art des Wegpunktes + (Teil %d) + + + Dein Track wird aufgezeichnet… + + + Nein + Ja + Fehler + Erfolgreich + Wegpunkt + + + (Beginn) + (Ende) + + + Anzeigen + An eine Google MyMaps-Karte senden + An eine neue Google MyMaps-Karte senden + An Google Docs senden + An Google senden… + Vom Gerät löschen… + Bearbeiten + Auf SD-Karte speichern… + GPX-Datei exportieren + KML-Datei exportieren + CSV-Datei exportieren + TCX-Datei exportieren + An Google MyMaps senden + Alle Tracks löschen + + Alle Tracks exportieren + Exportieren beendet + Einen Track auswählen. Lange berühren für mehr Optionen. + Alle Tracks + Dieser Track + Mit Freunden teilen… + Ein Link zur Google MyMaps-Karte + Eine GPX-Datei + Eine KML-Datei + Eine CSV-Datei + Eine TCX-Datei + Karte zurücksetzen. + + + Alle Wegpunkte löschen + Dieser Wegpunkt + Löschen + Details bearbeiten + Auf der Karte zeigen + Wegpunkt einfügen + Statistik einfügen + Wegpunkt auswählen. Lange berühren für mehr Optionen. + + + Trackdetails + Name + Beschreibung + Aktivität (z. B. Wandern, Fahrradfahren, Laufen, Klettern…) + + + Wegpunktdetails + Wegpunktname + Wegpunktbeschreibung + Art des Wegpunktes (z. B. Gebäude, Schlagloch, Sehenswürdigkeit…) + + + Einstellungen für My Tracks + Anzeige + Aufzeichnung + Mit Freunden teilen + + In dieser Android-Version nicht verfügbar + + Dauer zwischen Ansagen + Kürzeste Zeit zwischen zwei Ansagen + + Kleinste Entfernung zwischen Punkten + Kleinste Entfernung zwischen zwei Positionsaufzeichnungen + + Neues Segment, wenn Punkte weiter entfernt als + Der Track wird in zwei geteilt, wenn zwei folgende Punkte weiter entfernt sind als hier angegeben. + + Aufzeichnungsfrequenz + Kürzeste Zeit zwischen zwei Positionsaufzeichnungen + + Aufzeichnung pausieren, wenn Genauigkeit kleiner als + Die Aufzeichnung wird kurzfristig angehalten, wenn Deine Position nicht genauer als hier angegeben ermittelt werden kann. + + Entfernungen in metrischen Einheiten anzeigen + Alle Entfernungen werden in metrischen Einheiten angezeigt. + Alle Entfernungen werden in imperialen Längeneinheiten angezeigt. + + Karten auf maps.google.com veröffentlichen + Hochgeladene Karten auf maps.google.com sind für alle sichtbar. + Hochgeladene Karten auf maps.google.com werden nicht öffentlich sichtbar aufgelistet. + + Geschwindigkeitsanzeige + Geschwindigkeit wird angezeigt. + Pace wird angezeigt. + + Nur URL als Einladung zum Teilen senden + Beim Teilen mit Freunden wird nur die Karten-URL gesendet. + Beim Teilen mit Freunden wird der GPS-Track selbst gesendet. + + Zerteilungsfrequenz + Längste Dauer oder Entfernung, nach der automatisch ein Marker eingefügt wird. + + Zeitbegrenzung der Auto-Wiederaufnahme + Maximale Zeit in Minuten, nach der eine abgebrochene Aufzeichnung nach einem Telefonneustart wiederaufgenommen wird. + + Zeitstempel als Trackname + Der Trackname wird aus Datum und Zeit zum Aufzeichnungsbeginn zusammengesetzt. + Tracknamen erhalten eine fortlaufende Nummer. + + Pace + + Wird nur unter Android 2.0 oder höher unterstützt + Sensortyp + Der Typ des zu verbindenden Sensors + Sensor-Id + Die zum Sensor zu sendende Id + Die Id muss eine Ganzzahl sein. + Bluetooth-Sensor + Der zu verbindende Bluetoothsensor + Art der Datenanzeige + + Datensicherung (BETA) + SD-Karten-Sicherung + Jetzt sichern + Eine Kopie aller Deiner Tracks und Einstellungen auf die SD-Karte schreiben + Jetzt wiederherstellen + Alle Tracks und Einstellungen von der SD-Karte holen. + Während ein Track aufgezeichnet wird, kann nicht mit der Datensicherung gearbeitet werden. + + + Eine Wiederherstellung von der SD-Karte ÜBERSCHREIBT alle vorhandenen Tracks und Einstellungen. Willst Du das wirklich? + Wähle die wiederherzustellende Datensicherung aus + Keine Datensicherungen gefunden. + Schreibe alle Tracks und Einstellungen auf die SD-Karte. Dies kann eine Weile dauern... + Lese alle Tracks und Einstellungen von der SD-Karte. Dies kann eine Weile dauern... + + + GPS-Satelliten + + + Wähle eine Karte von Deinem Konto aus + Neue Karte erstellen… + An Google My Maps senden + Karten werden geladen… + Deine Karten konnten im Augenblick nicht geladen werden! + Diese Karte auswählen + Kartendetails bearbeiten + Karte mit Freunden teilen + Track mit Freunden teilen + Karte vom Konto löschen + Kartentitel + Trackbeschreibung + Diese Karte und ihren Inhalt in Suchergebnissen sichtbar machen. + Diese Karte und ihren Inhalt nicht in Suchergebnissen sichtbar machen. + Diese Karte und ihr Inhalt sind in Suchergebnissen sichtbar. + Weder diese Karte noch ihr Inhalt sind in Suchergebnissen sichtbar, allerdings kann sie trotzdem von anderen durch einen direkten Link angezeigt werden. + Schließen + Bearbeiten + Abbrechen + Sichern + + + Ich möchte gerne eine Karte mit Dir teilen. Vielleicht interessierst Du Dich für diese Karte: - - %s - Ich möchte einen GPS-Track mit Dir teilen - Vielleicht interessierst Du Dich für diesen GPS-Track. Er kann in Google Earth und anderen Programmen dargestellt werden. + + %s + Ich möchte einen GPS-Track mit Dir teilen + Vielleicht interessierst Du Dich für diese Track: + + %s. - - Öffentlich - Nicht in Suchergebnissen enthalten. - Du hast noch keine Karten. - - - - Durchschnitt in Bew. - Durchschnitt in Bew. - - - Durchschnittsgeschw. - Durchschnittstempo - - Höhenprofil - Höhengewinn - Höhe - Breitengrad - Längengrad - - - Niedrigste Höhenlage - Minimale Steigung - Schnellstes Tempo - - - Höchste Höhenlage - Maximale Steigung - Max. Geschwindigkeit - - Lade Track… - - - Diagramm-Einstellungen - - X-Achse - Nach Entfernung - Nach Zeit - - - Y-Achse - - - - Stunde - Stunden - Std - Min - Minute - Minuten - Sekunde - Sekunden - ms - - - m - km - Kilometer - Fuß - mi - Meilen - - - km/h - Kilometer pro Stunde - min/km - pro Kilometer - mi/h - Meilen pro Stunde - min/mi - pro Meile - - - W - - U/Min - bpm - - - Zeit in Bewegung - Gesamtzeit - Zeit - SD-Karte nicht verfügbar - Geschwindigkeit - Sie müssen erst eine Strecke zurücklegen. - Statistik - Gesamtentfernung - Entfernung - Kraft - Trittfrequenz - Puls - Maximalpuls - - - Bitte warten… - - - Lizenzvereinbarung für Endnutzer - Akzeptieren - Ablehnen - - - Über - Hilfe - Team: - Version: - Unterstützung - Unterstützung: - - - Senden an… - Google My Maps - Google Text & Tabellen - Jetzt senden - Neue Karte erstellen - Eine Karte auswählen - Trackstatistik senden - Statistik und Punkte senden - - - Kein Konto vorhanden - Kein Konto gefunden um Tracks zu versenden. - Wählen Sei ein Konto - - - Keine externes Speichermedium verfügbar. Stellen Sie sicher, dass eine SD-Karte vorhanden ist und nicht bereits von einem Computer darauf zugegriffen wird. - Die Datei kann nicht geschrieben werden. - Datei kann nicht gelesen werden. - Das Verzeichnis auf der SD-Karte kann nicht erstellt werden. - Der Track wurde auf die SD-Karte geschrieben. - Lesen der Daten von SD-Karte beendet. - - - Suche nach neuer Version - Jetzt aktualisieren - Fortfahren ohne zu aktualisieren - Ihre Version von %1$s funktioniert möglicherweise nicht mehr richtig und wird nicht mehr unterstützt. Bitte aktualisieren Sie %1$s. - Ihre Version von %1$s funktioniert möglicherweise nicht mehr richtig. Sie können sie jetzt aktualisieren, oder auf sie auf eigene Gefahr weiterverwenden. Einige Funktionen wie Datenaustausch mit Google-Diensten wird möglicherweise nicht mehr funktionieren, solange Sie die Software nicht aktualisieren. - Eine neue Version von %1$s ist jetzt verfügbar. Sie können Ihre aktuelle Version ohne Probleme weiterverwenden, aber um die neuesten Funktionen in Anspruch nehmen zu können, müssen sie die Software aktualisieren. - - - Willkommen - OK - Über My Tracks - - - My Tracks keine Entfernung + + Öffentlich + Nicht in Suchergebnissen enthalten. + Du hast noch keine Karten. + + + + Durchschnitt in Bew. + Pace in Bew. + + + Durchschnittsgeschw. + Durchschnitts-Pace + + Höhenprofil + Höhengewinn + Höhe + Breitengrad + Längengrad + + + Niedrigste Höhenlage + Minimale Steigung + Minimaler Pace + + + Höchste Höhenlage + Maximale Steigung + Max. Geschwindigkeit + + Lade Track… + + + Diagramm-Einstellungen + + X-Achse + Nach Entfernung + Nach Zeit + + + Y-Achse + + + + Stunde + Stunden + Std + Minute + Minuten + Sekunde + Sekunden + + + Fuß + Kilometer + Meilen + + + Kilometer pro Stunde + pro Kilometer + Meilen pro Stunde + pro Meile + + + Zeit in Bewegung + Gesamtzeit + Zeit + SD-Karte nicht verfügbar + Geschwindigkeit + Du musst erst eine Aufzeichnung beginnen. + Statistik + Gesamtentfernung + Entfernung + Energie + Kadenz + Herzfrequenz + Höchste Herzfrequenz + Bitte warten… + + + Lizenzvereinbarung für Endverbraucher + Akzeptieren + Ablehnen + + + Über + Hilfe + Unterstützung + + + Senden an… + Jetzt senden + Neue Karte erstellen + Eine Karte auswählen + Trackstatistik senden + Statistik und Punkte senden + + + Kein Konto vorhanden + Kein Konto gefunden, um Tracks zu versenden. + Wähle ein Konto + + + Keine externes Speichermedium verfügbar. Stelle sicher, dass eine SD-Karte vorhanden ist und nicht bereits von einem Computer darauf zugegriffen wird. + Die Datei kann nicht geschrieben werden. + Die Datei konnte nicht gelesen werden. + Das Verzeichnis auf der SD-Karte kann nicht erstellt werden. + Der Track wurde auf die SD-Karte geschrieben. + Lesen von der SD-Karte beendet. + + + Suche nach neuer Version + Jetzt aktualisieren + Fortfahren, ohne zu aktualisieren + Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig und wird nicht mehr unterstützt. Bitte aktualisiere %1$s. + Deine Version von %1$s funktioniert möglicherweise nicht mehr richtig. Du kannst sie jetzt aktualisieren, oder auf sie auf eigene Gefahr weiterverwenden. Einige Funktionen wie Datenaustausch mit Google-Diensten werden möglicherweise nicht mehr funktionieren, solange Du die Software nicht aktualisierst. + Eine neue Version von %1$s ist jetzt verfügbar. Du kannst Deine installierte Version ohne Probleme weiterverwenden, aber um die neuesten Funktionen in Anspruch nehmen zu können, musst Du die Software aktualisieren. + + + Willkommen + Über My Tracks + + + My Tracks keine Entfernung - My Tracks %1$s %2$.2f %3$s in %4$s mit %5$s %6$s - - - Hinweis zu Maßeinheiten - Per Voreinstellung werden metrische Einheiten verwendet. Möchten Sie das ändern? - - - Bluetooth - Bluetooth-Einstellungen - Gerätepaarung - Öffnen Sie die Bluetooth-Paarung der Android-Einstellungen - bluetoothSensorsEnabled - Sensor-Einstellungen - - - - Verbunden mit %s - Verbinden mit Bluetooth-Sensor... - Bluetooth-Sensor nicht verbunden - - - Sensor Data Time - Kraftmesser aktiviert - Pulsmesser aktiviert - Kein Sensor-Manager - Aktiviert - Verbinde - Verbunden - Nicht verbunden - Kein Sensor-Dienst - Suche - Sendet Daten - - - receive track recording notifications - read recorded track data - write recorded track data, start/stop recording tracks - Be notified when a track starts or stops being recorded. - Be able to read track data, such as points and waypoints. - Be able to write track data, such as starting recording or adding waypoints. - + --> + My Tracks %1$s %2$.2f %3$s in %4$s mit %5$s %6$s + + + Einheitenbenachrichtigung + Voreingestellt sind metrische Einheiten. Willst Du diese Einstellung ändern? + + + Bluetootheinstellungen + Geräteverbindung + Öffnet Androids Bluetootheinstellungen, um Geräte zu verbinden. + Sensoreinstellungen + + + Verbunden mit %s + Verbinde mit Bluetoothsensor ... + Bluetoothsensor nicht verbunden. + + + Sensor Data Time + Energiemessung aktiviert + Herzfrequenzmessung aktiviert + Keine Sensoren verfügbar + Aktiviert + Verbinde... + Verbunden + Getrennt + Kein Sensor-Dienst + Suche... + Sende... + + + Track-Aufzeichnungs-Benachrichtigungen empfangen + Aufgezeichnete Trackdaten lesen + Aufgezeichnete Trackdaten schreiben, Starten/Anhalten des Aufzeichnens von Tracks + Werde benachrichtigt, wenn die Aufzeichnung eines Tracks beginnt oder anhält. + Ermöglicht das Lesen von Trackdaten wie Messpunkten oder Wegpunkten. + Ermöglicht das Schreiben von Trackdaten wie beim Starten der Aufzeichnung oder beim Hinzufügen von Wegpunkten. + diff --git a/MyTracks/res/values/arrays.xml b/MyTracks/res/values/arrays.xml index 3eb41e8fe..964befc7d 100644 --- a/MyTracks/res/values/arrays.xml +++ b/MyTracks/res/values/arrays.xml @@ -393,12 +393,16 @@ limitations under the License. None + ANT+ Sensors + SRM ANT+ Bridge Zephyr Bluetooth @string/none + @string/ant_sensor_type + @string/srm_ant_bridge_sensor_type @string/zephyr_sensor_type diff --git a/MyTracks/res/values/do_not_translate.xml b/MyTracks/res/values/do_not_translate.xml new file mode 100644 index 000000000..32095d881 --- /dev/null +++ b/MyTracks/res/values/do_not_translate.xml @@ -0,0 +1,36 @@ + + + + + My Tracks + + + mytracks@googlegroups.com + mytracks.appspot.com + Copyright 2009 Google Inc. + Bartlomiej Niechwiej + Dylan Casey + Leif Hendrik Wilden + Rodrigo Damazio Bovendorp + Sandor Dornbush + + + NONE + ANT + SRM_ANT_BRIDGE + ZEPHYR + diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml index ebf7cdf07..b8e43f668 100644 --- a/MyTracks/res/values/keys.xml +++ b/MyTracks/res/values/keys.xml @@ -17,6 +17,9 @@ limitations under the License. announcementFrequency + antHeartRateSensorId + antPowerSensorId + antSRMBridgeSensorId autoResumeTrackTimeout bluetoothDevice bluetoothPairing diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index a347c8ae7..10a35a347 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -15,9 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. --> - - My Tracks - Record track Stop recording @@ -216,15 +213,15 @@ limitations under the License. Only supported in Android 2.0 or above Sensor Type - The type of sensor to connect to. - Sensor Id - The Id sent to sensors. - The Id must be an integer value. + The type of sensor to connect to + Clear ANT heart rate sensor pairing + Clear ANT power sensor pairing + Clear ANT srm bridge sensor pairing + ANT sensor not paired + ANT sensor paired with: %d Bluetooth sensor - The bluetooth sensor to connect to. + The bluetooth sensor to connect to Modular Stats Display - The stats screen is split in various modes. - Display all stats on one screen Backup (BETA) @@ -384,16 +381,6 @@ limitations under the License. Support Support: - - mytracks@googlegroups.com - mytracks.appspot.com - Copyright 2009 Google Inc. - Bartlomiej Niechwiej - Dylan Casey - Leif Hendrik Wilden - Rodrigo Damazio Bovendorp - Sandor Dornbush - Send to... Google My Maps @@ -454,10 +441,6 @@ limitations under the License. bluetoothSensorsEnabled Sensor Options - - NONE - ZEPHYR - Connected to %s Bluetooth sensor connecting ... diff --git a/MyTracks/res/xml/preferences.xml b/MyTracks/res/xml/preferences.xml index c0b519386..7e9ec97c9 100644 --- a/MyTracks/res/xml/preferences.xml +++ b/MyTracks/res/xml/preferences.xml @@ -122,6 +122,18 @@ limitations under the License. android:summary="@string/bluetooth_pairing_summary" android:persistent="false" /> + + + + + + diff --git a/MyTracks/src/com/dsi/ant/AntDefine.java b/MyTracks/src/com/dsi/ant/AntDefine.java new file mode 100644 index 000000000..4aabb7eb2 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/AntDefine.java @@ -0,0 +1,276 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + + + +/** + * The Android Ant API is not finalized, and *will* change. Use at your + * own risk. + * + * Public API for controlling the Ant Service. + * AntDefines contains definitions commonly used in ANT messaging. + * + * @hide + */ +public class AntDefine { + + ////////////////////////////////////////////// + // Library Version Information + ////////////////////////////////////////////// + public static final int ANT_LIBRARY_VERSION_CODE = 5; + public static final int ANT_LIBRARY_VERSION_MAJOR = 1; + public static final int ANT_LIBRARY_VERSION_MINOR = 4; + public static final String ANT_LIBRARY_VERSION_NAME = String.valueOf(ANT_LIBRARY_VERSION_MAJOR) + "." + String.valueOf(ANT_LIBRARY_VERSION_MINOR); + + ////////////////////////////////////////////// + // Valid Configuration Values + ////////////////////////////////////////////// + public static final byte MIN_BIN = 0; + public static final byte MAX_BIN = 10; + + public static final short MIN_DEVICE_ID = 0; + public static final short MAX_DEVICE_ID = (short)65535; + + public static final short MIN_BUFFER_THRESHOLD = 0; + public static final short MAX_BUFFER_THRESHOLD = 996; + + ////////////////////////////////////////////// + // ANT Message Payload Size + ////////////////////////////////////////////// + public static final byte ANT_STANDARD_DATA_PAYLOAD_SIZE =((byte)8); + + ////////////////////////////////////////////// + // ANT LIBRARY Extended Data Message Fields + // NOTE: You must check the extended message + // bitfield first to find out which fields + // are present before accessing them! + ////////////////////////////////////////////// + public static final byte ANT_EXT_MESG_DEVICE_ID_FIELD_SIZE =((byte)4); + //public static final byte ANT_EXT_STRING_SIZE =((byte)19); // this is the additional buffer space required required for setting USB Descriptor strings + public static final byte ANT_EXT_STRING_SIZE =((byte)0); // changed to 0 as we will not be dealing with ANT USB parts. + + ////////////////////////////////////////////// + // ANT Extended Data Message Bifield Definitions + ////////////////////////////////////////////// + public static final byte ANT_EXT_MESG_BITFIELD_DEVICE_ID =((byte)0x80); // first field after bitfield + public static final byte ANT_EXT_MESG_BITFIELD_RSSI =((byte)0x40); // next field after ID, if there is one + public static final byte ANT_EXT_MESG_BITFIELD_TIME_STAMP =((byte)0x20); // next field after RSSI, if there is one + + // 4 bits free reserved set to 0 + public static final byte ANT_EXT_MESG_BIFIELD_EXTENSION =((byte)0x01); + + // extended message input bitfield defines + public static final byte ANT_EXT_MESG_BITFIELD_OVERWRITE_SHARED_ADR =((byte)0x10); + public static final byte ANT_EXT_MESG_BITFIELD_TRANSMISSION_TYPE =((byte)0x08); + + ////////////////////////////////////////////// + // ID Definitions + ////////////////////////////////////////////// + public static final byte ANT_ID_SIZE =((byte)4); + public static final byte ANT_ID_TRANS_TYPE_OFFSET =((byte)3); + public static final byte ANT_ID_DEVICE_TYPE_OFFSET =((byte)2); + public static final byte ANT_ID_DEVICE_NUMBER_HIGH_OFFSET =((byte)1); + public static final byte ANT_ID_DEVICE_NUMBER_LOW_OFFSET =((byte)0); + public static final byte ANT_ID_DEVICE_TYPE_PAIRING_FLAG =((byte)0x80); + + public static final byte ANT_TRANS_TYPE_SHARED_ADDR_MASK =((byte)0x03); + public static final byte ANT_TRANS_TYPE_1_BYTE_SHARED_ADDRESS =((byte)0x02); + public static final byte ANT_TRANS_TYPE_2_BYTE_SHARED_ADDRESS =((byte)0x03); + + ////////////////////////////////////////////// + // Assign Channel Parameters + ////////////////////////////////////////////// + public static final byte PARAMETER_RX_NOT_TX =((byte)0x00); + public static final byte PARAMETER_TX_NOT_RX =((byte)0x10); + public static final byte PARAMETER_SHARED_CHANNEL =((byte)0x20); + public static final byte PARAMETER_NO_TX_GUARD_BAND =((byte)0x40); + public static final byte PARAMETER_ALWAYS_RX_WILD_CARD_SEARCH_ID =((byte)0x40); //Pre-AP2 + public static final byte PARAMETER_RX_ONLY =((byte)0x40); + + ////////////////////////////////////////////// + // Ext. Assign Channel Parameters + ////////////////////////////////////////////// + public static final byte EXT_PARAM_ALWAYS_SEARCH =((byte)0x01); + public static final byte EXT_PARAM_FREQUENCY_AGILITY =((byte)0x04); + + ////////////////////////////////////////////// + // Radio TX Power Definitions + ////////////////////////////////////////////// + public static final byte RADIO_TX_POWER_LVL_MASK =((byte)0x03); + + public static final byte RADIO_TX_POWER_LVL_0 =((byte)0x00); //(formerly: RADIO_TX_POWER_MINUS20DB); lowest + public static final byte RADIO_TX_POWER_LVL_1 =((byte)0x01); //(formerly: RADIO_TX_POWER_MINUS10DB); + public static final byte RADIO_TX_POWER_LVL_2 =((byte)0x02); //(formerly: RADIO_TX_POWER_MINUS5DB); + public static final byte RADIO_TX_POWER_LVL_3 =((byte)0x03); //(formerly: RADIO_TX_POWER_0DB); highest + + ////////////////////////////////////////////// + // Channel Status + ////////////////////////////////////////////// + public static final byte STATUS_CHANNEL_STATE_MASK =((byte)0x03); + public static final byte STATUS_UNASSIGNED_CHANNEL =((byte)0x00); + public static final byte STATUS_ASSIGNED_CHANNEL =((byte)0x01); + public static final byte STATUS_SEARCHING_CHANNEL =((byte)0x02); + public static final byte STATUS_TRACKING_CHANNEL =((byte)0x03); + + ////////////////////////////////////////////// + // Standard capabilities defines + ////////////////////////////////////////////// + public static final byte CAPABILITIES_NO_RX_CHANNELS =((byte)0x01); + public static final byte CAPABILITIES_NO_TX_CHANNELS =((byte)0x02); + public static final byte CAPABILITIES_NO_RX_MESSAGES =((byte)0x04); + public static final byte CAPABILITIES_NO_TX_MESSAGES =((byte)0x08); + public static final byte CAPABILITIES_NO_ACKD_MESSAGES =((byte)0x10); + public static final byte CAPABILITIES_NO_BURST_TRANSFER =((byte)0x20); + + ////////////////////////////////////////////// + // Advanced capabilities defines + ////////////////////////////////////////////// + public static final byte CAPABILITIES_OVERUN_UNDERRUN =((byte)0x01); // Support for this functionality has been dropped + public static final byte CAPABILITIES_NETWORK_ENABLED =((byte)0x02); + public static final byte CAPABILITIES_AP1_VERSION_2 =((byte)0x04); // This Version of the AP1 does not support transmit and only had a limited release + public static final byte CAPABILITIES_SERIAL_NUMBER_ENABLED =((byte)0x08); + public static final byte CAPABILITIES_PER_CHANNEL_TX_POWER_ENABLED =((byte)0x10); + public static final byte CAPABILITIES_LOW_PRIORITY_SEARCH_ENABLED =((byte)0x20); + public static final byte CAPABILITIES_SCRIPT_ENABLED =((byte)0x40); + public static final byte CAPABILITIES_SEARCH_LIST_ENABLED =((byte)0x80); + + ////////////////////////////////////////////// + // Advanced capabilities 2 defines + ////////////////////////////////////////////// + public static final byte CAPABILITIES_LED_ENABLED =((byte)0x01); + public static final byte CAPABILITIES_EXT_MESSAGE_ENABLED =((byte)0x02); + public static final byte CAPABILITIES_SCAN_MODE_ENABLED =((byte)0x04); + public static final byte CAPABILITIES_RESERVED =((byte)0x08); + public static final byte CAPABILITIES_PROX_SEARCH_ENABLED =((byte)0x10); + public static final byte CAPABILITIES_EXT_ASSIGN_ENABLED =((byte)0x20); + public static final byte CAPABILITIES_FREE_1 =((byte)0x40); + public static final byte CAPABILITIES_FIT1_ENABLED =((byte)0x80); + + ////////////////////////////////////////////// + // Advanced capabilities 3 defines + ////////////////////////////////////////////// + public static final byte CAPABILITIES_SENSRCORE_ENABLED =((byte)0x01); + public static final byte CAPABILITIES_RESERVED_1 =((byte)0x02); + public static final byte CAPABILITIES_RESERVED_2 =((byte)0x04); + public static final byte CAPABILITIES_RESERVED_3 =((byte)0x08); + + + ////////////////////////////////////////////// + // Burst Message Sequence + ////////////////////////////////////////////// + public static final byte CHANNEL_NUMBER_MASK =((byte)0x1F); + public static final byte SEQUENCE_NUMBER_MASK =((byte)0xE0); + public static final byte SEQUENCE_NUMBER_ROLLOVER =((byte)0x60); + public static final byte SEQUENCE_FIRST_MESSAGE =((byte)0x00); + public static final byte SEQUENCE_LAST_MESSAGE =((byte)0x80); + public static final byte SEQUENCE_NUMBER_INC =((byte)0x20); + + ////////////////////////////////////////////// + // Control Message Flags + ////////////////////////////////////////////// + public static final byte BROADCAST_CONTROL_BYTE =((byte)0x00); + public static final byte ACKNOWLEDGED_CONTROL_BYTE =((byte)0xA0); + + ////////////////////////////////////////////// + // Response / Event Codes + ////////////////////////////////////////////// + public static final byte RESPONSE_NO_ERROR =((byte)0x00); + public static final byte NO_EVENT =((byte)0x00); + + public static final byte EVENT_RX_SEARCH_TIMEOUT =((byte)0x01); + public static final byte EVENT_RX_FAIL =((byte)0x02); + public static final byte EVENT_TX =((byte)0x03); + public static final byte EVENT_TRANSFER_RX_FAILED =((byte)0x04); + public static final byte EVENT_TRANSFER_TX_COMPLETED =((byte)0x05); + public static final byte EVENT_TRANSFER_TX_FAILED =((byte)0x06); + public static final byte EVENT_CHANNEL_CLOSED =((byte)0x07); + public static final byte EVENT_RX_FAIL_GO_TO_SEARCH =((byte)0x08); + public static final byte EVENT_CHANNEL_COLLISION =((byte)0x09); + public static final byte EVENT_TRANSFER_TX_START =((byte)0x0A); // a pending transmit transfer has begun + + public static final byte EVENT_CHANNEL_ACTIVE =((byte)0x0F); + + public static final byte EVENT_TRANSFER_TX_NEXT_MESSAGE =((byte)0x11); // only enabled in FIT1 + + public static final byte CHANNEL_IN_WRONG_STATE =((byte)0x15); // returned on attempt to perform an action from the wrong channel state + public static final byte CHANNEL_NOT_OPENED =((byte)0x16); // returned on attempt to communicate on a channel that is not open + public static final byte CHANNEL_ID_NOT_SET =((byte)0x18); // returned on attempt to open a channel without setting the channel ID + public static final byte CLOSE_ALL_CHANNELS =((byte)0x19); // returned when attempting to start scanning mode, when channels are still open + + public static final byte TRANSFER_IN_PROGRESS =((byte)0x1F); // returned on attempt to communicate on a channel with a TX transfer in progress + public static final byte TRANSFER_SEQUENCE_NUMBER_ERROR =((byte)0x20); // returned when sequence number is out of order on a Burst transfer + public static final byte TRANSFER_IN_ERROR =((byte)0x21); + public static final byte TRANSFER_BUSY =((byte)0x22); + + public static final byte INVALID_MESSAGE_CRC =((byte)0x26); // returned if there is a framing error on an incomming message + public static final byte MESSAGE_SIZE_EXCEEDS_LIMIT =((byte)0x27); // returned if a data message is provided that is too large + public static final byte INVALID_MESSAGE =((byte)0x28); // returned when the message has an invalid parameter + public static final byte INVALID_NETWORK_NUMBER =((byte)0x29); // returned when an invalid network number is provided + public static final byte INVALID_LIST_ID =((byte)0x30); // returned when the provided list ID or size exceeds the limit + public static final byte INVALID_SCAN_TX_CHANNEL =((byte)0x31); // returned when attempting to transmit on channel 0 when in scan mode + + public static final byte INVALID_PARAMETER_PROVIDED =((byte)0x33); // returned when an invalid parameter is specified in a configuration message + + public static final byte EVENT_SERIAL_QUE_OVERFLOW =((byte)0x34); + public static final byte EVENT_QUE_OVERFLOW =((byte)0x35); // ANT event que has overflowed and lost 1 or more events + + public static final byte EVENT_CLK_ERROR =((byte)0x36); // debug event for XOSC16M on LE1 + + public static final byte SCRIPT_FULL_ERROR =((byte)0x40); // error writing to script, memory is full + public static final byte SCRIPT_WRITE_ERROR =((byte)0x41); // error writing to script, bytes not written correctly + public static final byte SCRIPT_INVALID_PAGE_ERROR =((byte)0x42); // error accessing script page + public static final byte SCRIPT_LOCKED_ERROR =((byte)0x43); // the scripts are locked and can't be dumped + + public static final byte NO_RESPONSE_MESSAGE =((byte)0x50); // returned to the Command_SerialMessageProcess function, so no reply message is generated + public static final byte RETURN_TO_MFG =((byte)0x51); // default return to any mesg when the module determines that the mfg procedure has not been fully completed + + public static final byte FIT_ACTIVE_SEARCH_TIMEOUT =((byte)0x60); // Fit1 only event added for timeout of the pairing state after the Fit module becomes active + public static final byte FIT_WATCH_PAIR =((byte)0x61); // Fit1 only + public static final byte FIT_WATCH_UNPAIR =((byte)0x62); // Fit1 only + + public static final byte USB_STRING_WRITE_FAIL =((byte)0x70); + + // Internal only events below this point + public static final byte INTERNAL_ONLY_EVENTS =((byte)0x80); + public static final byte EVENT_RX =((byte)0x80); // INTERNAL: Event for a receive message + public static final byte EVENT_NEW_CHANNEL =((byte)0x81); // INTERNAL: EVENT for a new active channel + public static final byte EVENT_PASS_THRU =((byte)0x82); // INTERNAL: Event to allow an upper stack events to pass through lower stacks + + public static final byte EVENT_BLOCKED =((byte)0xFF); // INTERNAL: Event to replace any event we do not wish to go out, will also zero the size of the Tx message + + /////////////////////////////////////////////////////// + // Script Command Codes + /////////////////////////////////////////////////////// + public static final byte SCRIPT_CMD_FORMAT =((byte)0x00); + public static final byte SCRIPT_CMD_DUMP =((byte)0x01); + public static final byte SCRIPT_CMD_SET_DEFAULT_SECTOR =((byte)0x02); + public static final byte SCRIPT_CMD_END_SECTOR =((byte)0x03); + public static final byte SCRIPT_CMD_END_DUMP =((byte)0x04); + public static final byte SCRIPT_CMD_LOCK =((byte)0x05); + + /////////////////////////////////////////////////////// + // Reset Mesg Codes + /////////////////////////////////////////////////////// + public static final byte RESET_FLAGS_MASK =((byte)0xE0); + public static final byte RESET_SUSPEND =((byte)0x80); // this must follow bitfield def + public static final byte RESET_SYNC =((byte)0x40); // this must follow bitfield def + public static final byte RESET_CMD =((byte)0x20); // this must follow bitfield def + public static final byte RESET_WDT =((byte)0x02); + public static final byte RESET_RST =((byte)0x01); + public static final byte RESET_POR =((byte)0x00); + +} \ No newline at end of file diff --git a/MyTracks/src/com/dsi/ant/AntInterface.java b/MyTracks/src/com/dsi/ant/AntInterface.java new file mode 100644 index 000000000..20d30349a --- /dev/null +++ b/MyTracks/src/com/dsi/ant/AntInterface.java @@ -0,0 +1,1134 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + package com.dsi.ant; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + + +// TODO: Auto-generated Javadoc + +/** + * Public API for controlling the Ant Service. AntInterface is a proxy + * object for controlling the Ant Service via IPC. Creating a AntInterface + * object will create a binding with the Ant service. + * + * @hide + */ +public class AntInterface { + + /** The Log Tag. */ + public static final String TAG = "ANTInterface"; + + /** Enable debug logging. */ + public static boolean DEBUG = false; + + /** Search string to find ANT Radio Proxy Service in the Android Marketplace */ + private static final String MARKET_SEARCH_TEXT_DEFAULT = "ANT Radio Service Dynastream Innovations Inc"; + + /** Inter-process communication with the ANT Radio Proxy Service. */ + public static IAnt sAntReceiver = null; + public static IServiceSettings sServiceSettingsReceiver = null; + + /** Singleton instance of this class. */ + private static AntInterface INSTANCE; + + /** Used when obtaining a reference to the singleton instance. */ + private static Object INSTANCE_LOCK = new Object(); + + /** The context to use. */ + private static Context sContext = null; + + /** Listens to changes to service connection status. */ + private static ServiceListener sServiceListener; + + /** Is the ANT Radio Proxy Service connected. */ + private static boolean sServiceConnected = false; + private static boolean sServiceSettingsConnected = false; + + private static int mServiceLibraryVersionCode = 0; + + /** + * An interface for notifying AntInterface IPC clients when they have + * been connected to the ANT service. + * + * @see ServiceEvent + */ + public interface ServiceListener + { + /** + * Called to notify the client when this proxy object has been + * connected to the ANT service. Clients must wait for + * this callback before making IPC calls on the ANT + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the ANT service. Clients must not + * make IPC calls on the ANT service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothAg service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + + + //Constructor + /** + * Instantiates a new ant interface. + * + * @param context the context + * @param listener the listener + * @since 1.0 + */ + private AntInterface(Context context, ServiceListener listener) + { + // This will be around as long as this process is + sContext = context; + sServiceListener = listener; + } + + /** + * Gets the single instance of AntInterface, creating it if it doesn't exist. + * + * @param context the context. + * @param listener the listener to be notified of status changes. + * @return the AntInterface instance. + * @since 1.0 + */ + public static AntInterface getInstance(Context context,ServiceListener listener) + { + if(DEBUG) Log.d(TAG, "getInstance"); + + synchronized (INSTANCE_LOCK) + { + if (INSTANCE == null) + { + if(DEBUG) Log.d(TAG, "getInstance: Creating new instance"); + + // TODO: rohan: bug, each new request for an instance will not have context and listener set to the requested objects + INSTANCE = new AntInterface(context,listener); + } + else + { + if(DEBUG) Log.d(TAG, "getInstance: Using existing instance"); + } + + if(!sServiceConnected) + { + if(DEBUG) Log.d(TAG, "getInstance: No connection to proxy service, attempting connection"); + + if(!INSTANCE.initService()) + { + Log.e(TAG, "getInstance: No connection to proxy service"); + + INSTANCE.destroy(); + INSTANCE = null; + } + } + + return INSTANCE; + } + } + + /** + * Go to market. + * + * @param pContext the context + * @param pSearchText the search text + * @since 1.2 + */ + public static void goToMarket(Context pContext, String pSearchText) + { + if(null == pSearchText) + { + goToMarket(pContext); + } + else + { + if(DEBUG) Log.i(TAG, "goToMarket: Search text = "+ pSearchText); + + Intent goToMarket = null; + goToMarket = new Intent(Intent.ACTION_VIEW,Uri.parse("http://market.android.com/search?q=" + pSearchText)); + goToMarket.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + pContext.startActivity(goToMarket); + } + } + + /** + * Go to market. + * + * @param pContext the context + * @since 1.2 + */ + public static void goToMarket(Context pContext) + { + if(DEBUG) Log.d(TAG, "goToMarket"); + + goToMarket(pContext, MARKET_SEARCH_TEXT_DEFAULT); + } + + /** + * Class for interacting with the ANT interface. + */ + private static ServiceConnection sIAntConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName pClassName, IBinder pService) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + if(DEBUG) Log.d(TAG, "sIAntConnection onServiceConnected()"); + sAntReceiver = IAnt.Stub.asInterface(pService); + + sServiceConnected = true; + + try + { + mServiceLibraryVersionCode = sAntReceiver.getServiceLibraryVersionCode(); + } + catch(RemoteException e) + { + Log.e(TAG, "sIAntConnection onServiceConnected: Could not get service library version"); + } + + switch(mServiceLibraryVersionCode) + { + // Fall through from newer versions + case 5: + { + if(!sServiceSettingsConnected) + { + boolean boundServiceSettings = sContext.bindService(new Intent(IServiceSettings.class.getName()), sIServiceSettingsConnection, Context.BIND_AUTO_CREATE); + Log.i(TAG, "sIAntConnection onServiceConnected: Bound with ANT Service Settings: " + boundServiceSettings); + } + else + { + if(DEBUG) Log.d(TAG, "sIAntConnection onServiceConnected: Already initialised Service Settings connection"); + } + } + } + + // Notify the attached application if it is registered + if (sServiceListener != null) + { + sServiceListener.onServiceConnected(); + } + else + { + if(DEBUG) Log.d(TAG, "sIAntConnection onServiceConnected: No service listener registered"); + } + } + + public void onServiceDisconnected(ComponentName pClassName) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + if(DEBUG) Log.d(TAG, "sIAntConnection onServiceDisconnected()"); + sAntReceiver = null; + + sServiceConnected = false; + + // Notify the attached application if it is registered + if (sServiceListener != null) + { + sServiceListener.onServiceDisconnected(); + } + else + { + if(DEBUG) Log.d(TAG, "sIAntConnection onServiceDisconnected: No service listener registered"); + } + + // Try and rebind to the service + INSTANCE.releaseService(); + INSTANCE.initService(); + } + }; + + /** + * Class for interacting with the ANT interface through the ANTLib version 5 functions. + */ + private static ServiceConnection sIServiceSettingsConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName pClassName, IBinder pService) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. We are communicating with our + // service through an IDL interface, so get a client-side + // representation of that from the raw service object. + if(DEBUG) Log.d(TAG, "sIServiceSettingsConnection onServiceConnected()"); + sServiceSettingsReceiver = IServiceSettings.Stub.asInterface(pService); + + sServiceSettingsConnected = true; + } + + public void onServiceDisconnected(ComponentName pClassName) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + if(DEBUG) Log.d(TAG, "sIServiceSettingsConnection onServiceDisconnected()"); + sServiceSettingsReceiver = null; + + sServiceSettingsConnected = false; + + // Try and rebind to the service + INSTANCE.releaseService(); + INSTANCE.initService(); + } + }; + + + /** + * Binds this activity to the ANT service. + * + * @return true, if successful + */ + private boolean initService() { + if(DEBUG) Log.d(TAG, "initService() entered"); + + boolean ret = false; + + if(!sServiceConnected) + { + ret = sContext.bindService(new Intent(IAnt.class.getName()), sIAntConnection, Context.BIND_AUTO_CREATE); + Log.i(TAG, "initService(): Bound with ANT service: " + ret); + } + else + { + if(DEBUG) Log.d(TAG, "initService: already initialised service"); + ret = true; + } + + return ret; + } + + /** Unbinds this activity from the ANT service. */ + private void releaseService() { + if(DEBUG) Log.d(TAG, "releaseService() entered"); + + if(sServiceConnected) + { + sContext.unbindService(sIAntConnection); + sServiceConnected = false; + } + + if(sServiceSettingsConnected) + { + sContext.unbindService(sIServiceSettingsConnection); + sServiceSettingsConnected = false; + } + + if(DEBUG) Log.d(TAG, "releaseService() unbound."); + } + + /** + * True if this activity can communicate with the ANT service. + * + * @return true, if is service connected + * @since 1.2 + */ + public boolean isServiceConnected() + { + return sServiceConnected; + } + + /** + * Destroy. + * + * @return true, if successful + * @since 1.0 + */ + public boolean destroy() + { + if(DEBUG) Log.d(TAG, "destroy"); + + releaseService(); + + INSTANCE = null; + + return true; + } + + + /** + * Ant service connection lost. + */ + private void antServiceConnectionLost() + { + Log.e(TAG, "Connection to ANT service lost"); + } + + /** + * Ant service connection to settings binder lost. + */ + private void antServiceSettingsConnectionLost() + { + Log.e(TAG, "Connection to ANT service settings lost"); + } + + + ////------------------------------------------------- + + /** + * Enable. + * + * @return true, if successful + * @since 1.0 + */ + public boolean enable() + { + if(DEBUG) Log.d(TAG, "enable"); + + if(!sServiceConnected) + { + // Haven't received 'onConnected' notification yet + return false; + } + + boolean result = false; + try { + result = sAntReceiver.enable(); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * Disable. + * + * @return true, if successful + * @since 1.0 + */ + public boolean disable() + { + if(DEBUG) Log.d(TAG, "disable"); + + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.disable(); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * Checks if is enabled. + * + * @return true, if is enabled + * @since 1.0 + */ + public boolean isEnabled() + { + if(DEBUG) Log.d(TAG, "isEnabled"); + + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.isEnabled(); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT tx message. + * + * @param message the message + * @return true, if successful + * @since 1.0 + */ + public boolean ANTTxMessage(byte[] message) + { + if(DEBUG) Log.d(TAG, "ANTTxMessage"); + + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTTxMessage(message); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT reset system. + * + * @return true, if successful + * @since 1.0 + */ + public boolean ANTResetSystem() + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTResetSystem(); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT unassign channel. + * + * @param channelNumber the channel number + * @return true, if successful + * @since 1.0 + */ + public boolean ANTUnassignChannel(byte channelNumber) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTUnassignChannel(channelNumber); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT assign channel. + * + * @param channelNumber the channel number + * @param channelType the channel type + * @param networkNumber the network number + * @return true, if successful + * @since 1.0 + */ + public boolean ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTAssignChannel(channelNumber, channelType, networkNumber); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set channel id. + * + * @param channelNumber the channel number + * @param deviceNumber the device number + * @param deviceType the device type + * @param txType the tx type + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, txType); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set channel period. + * + * @param channelNumber the channel number + * @param channelPeriod the channel period + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetChannelPeriod(byte channelNumber, short channelPeriod) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set channel rf freq. + * + * @param channelNumber the channel number + * @param radioFrequency the radio frequency + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetChannelRFFreq(channelNumber, radioFrequency); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set channel search timeout. + * + * @param channelNumber the channel number + * @param searchTimeout the search timeout + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetChannelSearchTimeout(channelNumber, searchTimeout); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set low priority channel search timeout. + * + * @param channelNumber the channel number + * @param searchTimeout the search timeout + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, searchTimeout); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set proximity search. + * + * @param channelNumber the channel number + * @param searchThreshold the search threshold + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetProximitySearch(byte channelNumber, byte searchThreshold) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetProximitySearch(channelNumber, searchThreshold); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT set channel transmit power + * @param channelNumber the channel number + * @param txPower the transmit power level + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSetChannelTxPower(byte channelNumber, byte txPower) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSetChannelTxPower(channelNumber, txPower); + } catch (RemoteException ex) + { + antServiceConnectionLost(); + } + return result; + } + + + /** + * ANT add channel id. + * + * @param channelNumber the channel number + * @param deviceNumber the device number + * @param deviceType the device type + * @param txType the tx type + * @param listIndex the list index + * @return true, if successful + * @since 1.0 + */ + public boolean ANTAddChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType, byte listIndex) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTAddChannelId(channelNumber, deviceNumber, deviceType, txType, listIndex); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT config list. + * + * @param channelNumber the channel number + * @param listSize the list size + * @param exclude the exclude + * @return true, if successful + * @since 1.0 + */ + public boolean ANTConfigList(byte channelNumber, byte listSize, byte exclude) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTConfigList(channelNumber, listSize, exclude); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT config event buffering. + * + * @param screenOnFlushTimerInterval the screen on flush timer interval + * @param screenOnFlushBufferThreshold the screen on flush buffer threshold + * @param screenOffFlushTimerInterval the screen off flush timer interval + * @param screenOffFlushBufferThreshold the screen off flush buffer threshold + * @return true, if successful + * @since 1.3 + */ + public boolean ANTConfigEventBuffering(short screenOnFlushTimerInterval, short screenOnFlushBufferThreshold, short screenOffFlushTimerInterval, short screenOffFlushBufferThreshold) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTConfigEventBuffering((int)screenOnFlushTimerInterval, (int)screenOnFlushBufferThreshold, (int)screenOffFlushTimerInterval, (int)screenOffFlushBufferThreshold); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT disable event buffering. + * + * @return true, if successful + * @since 1.1 + */ + public boolean ANTDisableEventBuffering() + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTDisableEventBuffering(); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT open channel. + * + * @param channelNumber the channel number + * @return true, if successful + * @since 1.0 + */ + public boolean ANTOpenChannel(byte channelNumber) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTOpenChannel(channelNumber); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT close channel. + * + * @param channelNumber the channel number + * @return true, if successful + * @since 1.0 + */ + public boolean ANTCloseChannel(byte channelNumber) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTCloseChannel(channelNumber); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT request message. + * + * @param channelNumber the channel number + * @param messageID the message id + * @return true, if successful + * @since 1.0 + */ + public boolean ANTRequestMessage(byte channelNumber, byte messageID) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTRequestMessage(channelNumber, messageID); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT send broadcast data. + * + * @param channelNumber the channel number + * @param txBuffer the tx buffer + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSendBroadcastData(byte channelNumber, byte[] txBuffer) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSendBroadcastData(channelNumber, txBuffer); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT send acknowledged data. + * + * @param channelNumber the channel number + * @param txBuffer the tx buffer + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSendAcknowledgedData(byte channelNumber, byte[] txBuffer) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSendAcknowledgedData(channelNumber, txBuffer); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * ANT send burst transfer packet. + * + * @param control the control + * @param txBuffer the tx buffer + * @return true, if successful + * @since 1.0 + */ + public boolean ANTSendBurstTransferPacket(byte control, byte[] txBuffer) + { + if(!sServiceConnected) + { + return false; + } + + boolean result = false; + try { + result = sAntReceiver.ANTSendBurstTransferPacket(control, txBuffer); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + return result; + } + + /** + * Transmits the given data on channelNumber as part of a burst message. + * + * @param channelNumber Which channel to transmit on. + * @param txBuffer The data to send. + * @param initialPacket Which packet in the burst sequence does the data begin in, 1 is the first. + * @param containsEndOfBurst Is this the last of the data to be sent in burst. + * @return The number of bytes still to be sent (approximately). 0 if success. + */ + public int ANTSendBurstTransfer(byte channelNumber, byte[] txBuffer) + { + int result = txBuffer.length; + + if(sServiceConnected) + { + try { + result = sAntReceiver.ANTSendBurstTransfer(channelNumber, txBuffer); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + } + return result; + } + + /** + * ANT send partial burst. + * + * @param channelNumber the channel number + * @param txBuffer the tx buffer + * @param initialPacket the initial packet + * @param containsEndOfBurst the contains end of burst + * @return the int + * @since 1.0 + */ + public int ANTSendPartialBurst(byte channelNumber, byte[] txBuffer, int initialPacket, boolean containsEndOfBurst) + { + int result = txBuffer.length; + + if(sServiceConnected) + { + try { + result = sAntReceiver.ANTTransmitBurst(channelNumber, txBuffer, initialPacket, containsEndOfBurst); + } catch (RemoteException ex) { + antServiceConnectionLost(); + } + } + return result; + } + + /** + * Returns the version code (eg. 1) of ANTLib used by the ANT application service + * + * @return the service library version code + * @throws RemoteException the remote exception + * @since 1.2 + */ + public int getServiceLibraryVersionCode() throws RemoteException + { + if(mServiceLibraryVersionCode == 0) + { + mServiceLibraryVersionCode = sAntReceiver.getServiceLibraryVersionCode(); + } + + return mServiceLibraryVersionCode; + } + + /** + * Returns the version name (eg "1.0") of ANTLib used by the ANT application service + * + * @return the service library version name + * @throws RemoteException the remote exception + * @since 1.2 + */ + public String getServiceLibraryVersionName() throws RemoteException + { + return sAntReceiver.getServiceLibraryVersionName(); + } + + + // + // ------------------- The below functions are for debugging/development and will likely disappear. + // + + /** + * Turn verbose logging on or off in the service. + * + * @param debug Whether debug logging should be enabled. + * @return true, if successful + * @since 1.4 + */ + public boolean debugLogging(boolean debug) + { + if(DEBUG) Log.d(TAG, "debugLogging"); + + if(!sServiceSettingsConnected) + { + // Haven't received 'onConnected' notification yet + return false; + } + + boolean result = false; + try { + sServiceSettingsReceiver.debugLogging(debug); + result = true; + } catch (RemoteException ex) { + antServiceSettingsConnectionLost(); + } + return result; + } + + /** + * Set how many ANT packets should be combined in one request to the hardware during a burst transfer. + * + * @param numPackets Combined packet count. + * @return true, if successful + * @since 1.4 + */ + boolean setNumCombinedBurstPackets(int numPackets) + { + if(DEBUG) Log.d(TAG, "setNumCombinedBurstPackets"); + + if(!sServiceSettingsConnected) + { + // Haven't received 'onConnected' notification yet + return false; + } + + boolean result = false; + try { + sServiceSettingsReceiver.setNumCombinedBurstPackets(numPackets); + result = true; + } catch (RemoteException ex) { + antServiceSettingsConnectionLost(); + } + + return result; + } + + /** + * Get the maximum number of ANT packets which will be combined in one request to the hardware during a burst transfer. + * + * @return The number of combined packets, or -1 on failure. + * @since 1.4 + */ + int getNumCombinedBurstPackets() + { + if(DEBUG) Log.d(TAG, "getNumCombinedBurstPackets"); + + if(!sServiceSettingsConnected) + { + // Haven't received 'onConnected' notification yet + return -1; + } + + int result = -1; + try { + result = sServiceSettingsReceiver.getNumCombinedBurstPackets(); + } catch (RemoteException ex) { + antServiceSettingsConnectionLost(); + } + + return result; + } +} diff --git a/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java b/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java new file mode 100644 index 000000000..1b0b7f7e7 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java @@ -0,0 +1,34 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + +/** + * Manages the ANT Interface + * + * @hide + */ +public interface AntInterfaceIntent { + + public static final String STATUS = "com.dsi.ant.rx.intent.STATUS"; + public static final String ANT_MESSAGE = "com.dsi.ant.intent.ANT_MESSAGE"; + + public static final String ANT_ENABLED_ACTION = "com.dsi.ant.intent.action.ANT_ENABLED"; + public static final String ANT_DISABLED_ACTION = "com.dsi.ant.intent.action.ANT_DISABLED"; + public static final String ANT_RESET_ACTION = "com.dsi.ant.intent.action.ANT_RESET"; + + public static final String ANT_RX_MESSAGE_ACTION = "com.dsi.ant.intent.action.ANT_RX_MESSAGE_ACTION"; + +} diff --git a/MyTracks/src/com/dsi/ant/AntMesg.java b/MyTracks/src/com/dsi/ant/AntMesg.java new file mode 100644 index 000000000..cf2ae70d9 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/AntMesg.java @@ -0,0 +1,279 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + +/** + * Public API for controlling the Ant Service. + * AntMesg contains definitions for ANT message IDs + * + * @hide + */ +public class AntMesg { + + ///////////////////////////////////////////////////////////////////////////// + // HCI VS Message Format + // Messages are in the format: + // + // Outgoing ANT messages (host -> ANT chip) + // 01 D1 FD XX YY YY II JJ ------ + // ^ ^ ^ ^ + // | HCI framing | | ANT Mesg | + // + // where: 01 is the 1 byte HCI packet Identifier (HCI Command packet) + // D1 FD is the 2 byte HCI op code (0xFDD1 stored in little endian) + // XX is the 1 byte Length of all parameters in bytes (number of bytes in the HCI packet after this byte) + // YY YY is the 2 byte Parameter describing the length of the entire ANT message (II JJ ------) stored in little endian + // II is the 1 byte size of the ANT message (0-249) + // JJ is the 1 byte ID of the ANT message (1-255, 0 is invalid) + // ------ is the data of the ANT message (0-249 bytes of data) + // + // Incoming HCI Command Complete for ANT message command (host <- ANT chip) + // 04 0E 04 01 D1 FD ZZ + // + // where: 04 is the 1 byte HCI packet Identifier (HCI Event packet) + // 0E is the 1 byte HCI event (Command Complete) + // 04 is the 1 byte Length of all parameters in bytes (there are 4 bytes) + // 01 is the 1 byte Number of parameters in the packet (there is 1 parameter) + // D1 FD is the 2 byte HCI Op code of the command (0xFDD1 stored in little endian) + // ZZ is the 1 byte response to the command (0x00 - Command Successful + // 0x1F - Returned if the receive message queue of the ANT chip is full, the command should be retried + // Other - Any other non-zero response code indicates an error) + // + // Incoming ANT messages (host <- ANT chip) + // 04 FF XX 00 05 YY YY II JJ ------ + // ^ ^ ^ ^ + // | HCI framing | | ANT Mesg | + // + // where: 04 is the 1 byte HCI packet Identifier (HCI Event packet) + // FF is the 1 byte HCI event code (0xFF Vendor Specific event) + // XX is the 1 byte Length of all parameters in bytes (number of bytes in the HCI packet after this byte) + // 00 05 is the 2 byte vendor specific event code for ANT messages (0x0500 stored in little endian) + // YY YY is the 2 byte Parameter describing the length of the entire ANT message (II JJ ------) stored in little endian + // II is the 1 byte size of the ANT message (0-249) + // JJ is the 1 byte ID of the ANT message (1-255, 0 is invalid) + // ------ is the data of the ANT message (0-249 bytes of data) + // + ///////////////////////////////////////////////////////////////////////////// + + public static final byte MESG_SYNC_SIZE =((byte)0); // Ant messages are embedded in HCI messages we do not include a sync byte + public static final byte MESG_SIZE_SIZE =((byte)1); + public static final byte MESG_ID_SIZE =((byte)1); + public static final byte MESG_CHANNEL_NUM_SIZE =((byte)1); + public static final byte MESG_EXT_MESG_BF_SIZE =((byte)1); // NOTE: this could increase in the future + public static final byte MESG_CHECKSUM_SIZE =((byte)0); // Ant messages are embedded in HCI messages we do not include a checksum + public static final byte MESG_DATA_SIZE =((byte)9); + + // The largest serial message is an ANT data message with all of the extended fields + public static final byte MESG_ANT_MAX_PAYLOAD_SIZE =AntDefine.ANT_STANDARD_DATA_PAYLOAD_SIZE; + + public static final byte MESG_MAX_EXT_DATA_SIZE =(AntDefine.ANT_EXT_MESG_DEVICE_ID_FIELD_SIZE + AntDefine.ANT_EXT_STRING_SIZE); // ANT device ID (4 bytes) + Padding for ANT EXT string size(19 bytes) + + public static final byte MESG_MAX_DATA_SIZE =(MESG_ANT_MAX_PAYLOAD_SIZE + MESG_EXT_MESG_BF_SIZE + MESG_MAX_EXT_DATA_SIZE); // ANT data payload (8 bytes) + extended bitfield (1 byte) + extended data (10 bytes) + public static final byte MESG_MAX_SIZE_VALUE =(MESG_MAX_DATA_SIZE + MESG_CHANNEL_NUM_SIZE); // this is the maximum value that the serial message size value is allowed to be + public static final byte MESG_BUFFER_SIZE =(MESG_SIZE_SIZE + MESG_ID_SIZE + MESG_CHANNEL_NUM_SIZE + MESG_MAX_DATA_SIZE + MESG_CHECKSUM_SIZE); + public static final byte MESG_FRAMED_SIZE =(MESG_ID_SIZE + MESG_CHANNEL_NUM_SIZE + MESG_MAX_DATA_SIZE); + public static final byte MESG_HEADER_SIZE =(MESG_SYNC_SIZE + MESG_SIZE_SIZE + MESG_ID_SIZE); + public static final byte MESG_FRAME_SIZE =(MESG_HEADER_SIZE + MESG_CHECKSUM_SIZE); + public static final byte MESG_MAX_SIZE =(MESG_MAX_DATA_SIZE + MESG_FRAME_SIZE); + + public static final byte MESG_SIZE_OFFSET =(MESG_SYNC_SIZE); + public static final byte MESG_ID_OFFSET =(MESG_SYNC_SIZE + MESG_SIZE_SIZE); + public static final byte MESG_DATA_OFFSET =(MESG_HEADER_SIZE); + public static final byte MESG_RECOMMENDED_BUFFER_SIZE =((byte) 64); // This is the recommended size for serial message buffers if there are no RAM restrictions on the system + + ////////////////////////////////////////////// + // Message ID's + ////////////////////////////////////////////// + public static final byte MESG_INVALID_ID =((byte)0x00); + public static final byte MESG_EVENT_ID =((byte)0x01); + + public static final byte MESG_VERSION_ID =((byte)0x3E); + public static final byte MESG_RESPONSE_EVENT_ID =((byte)0x40); + + public static final byte MESG_UNASSIGN_CHANNEL_ID =((byte)0x41); + public static final byte MESG_ASSIGN_CHANNEL_ID =((byte)0x42); + public static final byte MESG_CHANNEL_MESG_PERIOD_ID =((byte)0x43); + public static final byte MESG_CHANNEL_SEARCH_TIMEOUT_ID =((byte)0x44); + public static final byte MESG_CHANNEL_RADIO_FREQ_ID =((byte)0x45); + public static final byte MESG_NETWORK_KEY_ID =((byte)0x46); + public static final byte MESG_RADIO_TX_POWER_ID =((byte)0x47); + public static final byte MESG_RADIO_CW_MODE_ID =((byte)0x48); + + public static final byte MESG_SYSTEM_RESET_ID =((byte)0x4A); + public static final byte MESG_OPEN_CHANNEL_ID =((byte)0x4B); + public static final byte MESG_CLOSE_CHANNEL_ID =((byte)0x4C); + public static final byte MESG_REQUEST_ID =((byte)0x4D); + + public static final byte MESG_BROADCAST_DATA_ID =((byte)0x4E); + public static final byte MESG_ACKNOWLEDGED_DATA_ID =((byte)0x4F); + public static final byte MESG_BURST_DATA_ID =((byte)0x50); + + public static final byte MESG_CHANNEL_ID_ID =((byte)0x51); + public static final byte MESG_CHANNEL_STATUS_ID =((byte)0x52); + public static final byte MESG_RADIO_CW_INIT_ID =((byte)0x53); + public static final byte MESG_CAPABILITIES_ID =((byte)0x54); + + public static final byte MESG_STACKLIMIT_ID =((byte)0x55); + + public static final byte MESG_SCRIPT_DATA_ID =((byte)0x56); + public static final byte MESG_SCRIPT_CMD_ID =((byte)0x57); + + public static final byte MESG_ID_LIST_ADD_ID =((byte)0x59); + public static final byte MESG_ID_LIST_CONFIG_ID =((byte)0x5A); + public static final byte MESG_OPEN_RX_SCAN_ID =((byte)0x5B); + + public static final byte MESG_EXT_CHANNEL_RADIO_FREQ_ID =((byte)0x5C); // OBSOLETE: (for 905 radio) + public static final byte MESG_EXT_BROADCAST_DATA_ID =((byte)0x5D); + public static final byte MESG_EXT_ACKNOWLEDGED_DATA_ID =((byte)0x5E); + public static final byte MESG_EXT_BURST_DATA_ID =((byte)0x5F); + + public static final byte MESG_CHANNEL_RADIO_TX_POWER_ID =((byte)0x60); + public static final byte MESG_GET_SERIAL_NUM_ID =((byte)0x61); + public static final byte MESG_GET_TEMP_CAL_ID =((byte)0x62); + public static final byte MESG_SET_LP_SEARCH_TIMEOUT_ID =((byte)0x63); + public static final byte MESG_SET_TX_SEARCH_ON_NEXT_ID =((byte)0x64); + public static final byte MESG_SERIAL_NUM_SET_CHANNEL_ID_ID =((byte)0x65); + public static final byte MESG_RX_EXT_MESGS_ENABLE_ID =((byte)0x66); + public static final byte MESG_RADIO_CONFIG_ALWAYS_ID =((byte)0x67); + public static final byte MESG_ENABLE_LED_FLASH_ID =((byte)0x68); + + public static final byte MESG_XTAL_ENABLE_ID =((byte)0x6D); + + public static final byte MESG_STARTUP_MESG_ID =((byte)0x6F); + public static final byte MESG_AUTO_FREQ_CONFIG_ID =((byte)0x70); + public static final byte MESG_PROX_SEARCH_CONFIG_ID =((byte)0x71); + public static final byte MESG_EVENT_BUFFERING_CONFIG_ID =((byte)0x74); + + + public static final byte MESG_CUBE_CMD_ID =((byte)0x80); + + public static final byte MESG_GET_PIN_DIODE_CONTROL_ID =((byte)0x8D); + public static final byte MESG_PIN_DIODE_CONTROL_ID =((byte)0x8E); + public static final byte MESG_FIT1_SET_AGC_ID =((byte)0x8F); + + public static final byte MESG_FIT1_SET_EQUIP_STATE_ID =((byte)0x91); // *** CONFLICT: w/ Sensrcore, Fit1 will never have sensrcore enabled + + // Sensrcore Messages + public static final byte MESG_SET_CHANNEL_INPUT_MASK_ID =((byte)0x90); + public static final byte MESG_SET_CHANNEL_DATA_TYPE_ID =((byte)0x91); + public static final byte MESG_READ_PINS_FOR_SECT_ID =((byte)0x92); + public static final byte MESG_TIMER_SELECT_ID =((byte)0x93); + public static final byte MESG_ATOD_SETTINGS_ID =((byte)0x94); + public static final byte MESG_SET_SHARED_ADDRESS_ID =((byte)0x95); + public static final byte MESG_ATOD_EXTERNAL_ENABLE_ID =((byte)0x96); + public static final byte MESG_ATOD_PIN_SETUP_ID =((byte)0x97); + public static final byte MESG_SETUP_ALARM_ID =((byte)0x98); + public static final byte MESG_ALARM_VARIABLE_MODIFY_TEST_ID =((byte)0x99); + public static final byte MESG_PARTIAL_RESET_ID =((byte)0x9A); + public static final byte MESG_OVERWRITE_TEMP_CAL_ID =((byte)0x9B); + public static final byte MESG_SERIAL_PASSTHRU_SETTINGS_ID =((byte)0x9C); + + public static final byte MESG_BIST_ID =((byte)0xAA); + + public static final byte MESG_UNLOCK_INTERFACE_ID =((byte)0xAD); + public static final byte MESG_SERIAL_ERROR_ID =((byte)0xAE); + public static final byte MESG_SET_ID_STRING_ID =((byte)0xAF); + + public static final byte MESG_PORT_GET_IO_STATE_ID =((byte)0xB4); + public static final byte MESG_PORT_SET_IO_STATE_ID =((byte)0xB5); + + public static final byte MESG_SLEEP_ID =((byte)0xC5); + public static final byte MESG_GET_GRMN_ESN_ID =((byte)0xC6); + public static final byte MESG_SET_USB_INFO_ID =((byte)0xC7); + + public static final byte MESG_COMMAND_COMPLETE_RESPONSE_ID =((byte)0xC8); + + ////////////////////////////////////////////// + // Command complete results + ////////////////////////////////////////////// + public static final byte MESG_COMMAND_COMPLETE_SUCCESS =((byte)0x00); + public static final byte MESG_COMMAND_COMPLETE_RETRY =((byte)0x1F); + + ////////////////////////////////////////////// + // Message Sizes + ////////////////////////////////////////////// + public static final byte MESG_INVALID_SIZE =((byte)0); + + public static final byte MESG_VERSION_SIZE =((byte)13); + public static final byte MESG_RESPONSE_EVENT_SIZE =((byte)3); + public static final byte MESG_CHANNEL_STATUS_SIZE =((byte)2); + + public static final byte MESG_UNASSIGN_CHANNEL_SIZE =((byte)1); + public static final byte MESG_ASSIGN_CHANNEL_SIZE =((byte)3); + public static final byte MESG_CHANNEL_ID_SIZE =((byte)5); + public static final byte MESG_CHANNEL_MESG_PERIOD_SIZE =((byte)3); + public static final byte MESG_CHANNEL_SEARCH_TIMEOUT_SIZE =((byte)2); + public static final byte MESG_CHANNEL_RADIO_FREQ_SIZE =((byte)2); + public static final byte MESG_CHANNEL_RADIO_TX_POWER_SIZE =((byte)2); + public static final byte MESG_NETWORK_KEY_SIZE =((byte)9); + public static final byte MESG_RADIO_TX_POWER_SIZE =((byte)2); + public static final byte MESG_RADIO_CW_MODE_SIZE =((byte)3); + public static final byte MESG_RADIO_CW_INIT_SIZE =((byte)1); + + public static final byte MESG_SYSTEM_RESET_SIZE =((byte)1); + public static final byte MESG_OPEN_CHANNEL_SIZE =((byte)1); + public static final byte MESG_CLOSE_CHANNEL_SIZE =((byte)1); + public static final byte MESG_REQUEST_SIZE =((byte)2); + + public static final byte MESG_CAPABILITIES_SIZE =((byte)6); + public static final byte MESG_STACKLIMIT_SIZE =((byte)2); + + public static final byte MESG_SCRIPT_DATA_SIZE =((byte)10); + public static final byte MESG_SCRIPT_CMD_SIZE =((byte)3); + + public static final byte MESG_ID_LIST_ADD_SIZE =((byte)6); + public static final byte MESG_ID_LIST_CONFIG_SIZE =((byte)3); + public static final byte MESG_OPEN_RX_SCAN_SIZE =((byte)1); + public static final byte MESG_EXT_CHANNEL_RADIO_FREQ_SIZE =((byte)3); + + public static final byte MESG_RADIO_CONFIG_ALWAYS_SIZE =((byte)2); + public static final byte MESG_RX_EXT_MESGS_ENABLE_SIZE =((byte)2); + public static final byte MESG_SET_TX_SEARCH_ON_NEXT_SIZE =((byte)2); + public static final byte MESG_SET_LP_SEARCH_TIMEOUT_SIZE =((byte)2); + + public static final byte MESG_SERIAL_NUM_SET_CHANNEL_ID_SIZE =((byte)3); + public static final byte MESG_ENABLE_LED_FLASH_SIZE =((byte)2); + public static final byte MESG_GET_SERIAL_NUM_SIZE =((byte)4); + public static final byte MESG_GET_TEMP_CAL_SIZE =((byte)4); + + public static final byte MESG_XTAL_ENABLE_SIZE =((byte)1); + public static final byte MESG_STARTUP_MESG_SIZE =((byte)1); + public static final byte MESG_AUTO_FREQ_CONFIG_SIZE =((byte)4); + public static final byte MESG_PROX_SEARCH_CONFIG_SIZE =((byte)2); + + public static final byte MESG_GET_PIN_DIODE_CONTROL_SIZE =((byte)1); + public static final byte MESG_PIN_DIODE_CONTROL_ID_SIZE =((byte)2); + public static final byte MESG_FIT1_SET_EQUIP_STATE_SIZE =((byte)2); + public static final byte MESG_FIT1_SET_AGC_SIZE =((byte)3); + + public static final byte MESG_BIST_SIZE =((byte)6); + public static final byte MESG_UNLOCK_INTERFACE_SIZE =((byte)1); + public static final byte MESG_SET_SHARED_ADDRESS_SIZE =((byte)3); + + public static final byte MESG_GET_GRMN_ESN_SIZE =((byte)5); + + public static final byte MESG_PORT_SET_IO_STATE_SIZE =((byte)5); + + public static final byte MESG_EVENT_BUFFERING_CONFIG_SIZE =((byte)6); + + public static final byte MESG_SLEEP_SIZE =((byte)1); + + + public static final byte MESG_EXT_DATA_SIZE =((byte)13); + + protected AntMesg() + { } +} diff --git a/MyTracks/src/com/dsi/ant/IAnt.aidl b/MyTracks/src/com/dsi/ant/IAnt.aidl new file mode 100644 index 000000000..8ee999070 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/IAnt.aidl @@ -0,0 +1,70 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + +/** + * The Android Ant API is not finalized, and *will* be updated and expanded. + * This is the base level ANT messaging API and gives any application full + * control over the ANT radio HW. Caution should be exercised when using + * this interface. + * + * Public API for controlling the Ant Service. + * + * {@hide} + */ +interface IAnt { +// Since version 1 (1.0): + boolean enable(); + boolean disable(); + boolean isEnabled(); + + boolean ANTTxMessage(in byte[] message); + + boolean ANTResetSystem(); + boolean ANTUnassignChannel(byte channelNumber); + boolean ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber); + boolean ANTSetChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType); + boolean ANTSetChannelPeriod(byte channelNumber, int channelPeriod); + boolean ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency); + boolean ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout); + boolean ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout); + boolean ANTSetProximitySearch(byte channelNumber, byte searchThreshold); + boolean ANTSetChannelTxPower(byte channelNumber, byte txPower); + boolean ANTAddChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType, byte listIndex); + boolean ANTConfigList(byte channelNumber, byte listSize, byte exclude); + + boolean ANTOpenChannel(byte channelNumber); + boolean ANTCloseChannel(byte channelNumber); + + boolean ANTRequestMessage(byte channelNumber, byte messageID); + boolean ANTSendBroadcastData(byte channelNumber, in byte[] txBuffer); + boolean ANTSendAcknowledgedData(byte channelNumber, in byte[] txBuffer); + + boolean ANTSendBurstTransferPacket(byte control, in byte[] txBuffer); + + int ANTSendBurstTransfer(byte channelNumber, in byte[] txBuffer); + int ANTTransmitBurst(byte channelNumber, in byte[] txBuffer, int initialPacket, boolean containsEndOfBurst); + +// Since version 4 (1.3): + boolean ANTConfigEventBuffering(int screenOnFlushTimerInterval, int screenOnFlushBufferThreshold, int screenOffFlushTimerInterval, int screenOffFlushBufferThreshold); + +// Since version 2 (1.1): + boolean ANTDisableEventBuffering(); + +// Since version 3 (1.2): + int getServiceLibraryVersionCode(); + String getServiceLibraryVersionName(); +} diff --git a/MyTracks/src/com/dsi/ant/IServiceSettings.aidl b/MyTracks/src/com/dsi/ant/IServiceSettings.aidl new file mode 100644 index 000000000..3d0cf61c8 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/IServiceSettings.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2010 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + +/** + * + * API for configuring the non-ANT components of the ANT Service. + * + * {@hide} + */ +interface IServiceSettings { + void debugLogging(boolean debug); + + boolean setNumCombinedBurstPackets(int numPackets); + int getNumCombinedBurstPackets(); +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/AntPreference.java b/MyTracks/src/com/google/android/apps/mytracks/AntPreference.java new file mode 100644 index 000000000..6d6a4b853 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/AntPreference.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks; + +import com.google.android.maps.mytracks.R; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; + +/** + * A preference for an ANT device pairing. + * Currently this shows the ID and lets the user clear that ID for future pairing. + * TODO: Support pairing from this preference. + * + * @author Sandor Dornbush + */ +public class AntPreference extends Preference { + + public AntPreference(Context context) { + super(context); + init(); + } + + public AntPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + int sensorId = getPersistedInt(0); + if (sensorId == 0) { + setSummary(R.string.settings_ant_not_paired); + } else { + setSummary(String.format(getContext().getString(R.string.settings_ant_paired), sensorId)); + } + + // Add actions to allow repairing. + setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + AntPreference.this.persistInt(0); + setSummary(R.string.settings_ant_not_paired); + return true; + } + }); + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/CheckUnits.java b/MyTracks/src/com/google/android/apps/mytracks/CheckUnits.java index 5947f8900..d1fc56029 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/CheckUnits.java +++ b/MyTracks/src/com/google/android/apps/mytracks/CheckUnits.java @@ -16,6 +16,7 @@ package com.google.android.apps.mytracks; +import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.maps.mytracks.R; import android.app.Activity; @@ -82,7 +83,8 @@ class CheckUnits { } private static void recordCheckPerformed(SharedPreferences preferences) { - preferences.edit().putBoolean(PREFERENCE_UNITS_CHECKED, true).commit(); + ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges( + preferences.edit().putBoolean(PREFERENCE_UNITS_CHECKED, true)); } private CheckUnits() { diff --git a/MyTracks/src/com/google/android/apps/mytracks/Eula.java b/MyTracks/src/com/google/android/apps/mytracks/Eula.java index f070e310a..4471d93fc 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/Eula.java +++ b/MyTracks/src/com/google/android/apps/mytracks/Eula.java @@ -15,6 +15,7 @@ */ package com.google.android.apps.mytracks; +import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.ResourceUtils; import com.google.android.maps.mytracks.R; @@ -77,7 +78,8 @@ class Eula { } private static void accept(Activity activity, SharedPreferences preferences) { - preferences.edit().putBoolean(PREFERENCE_EULA_ACCEPTED, true).commit(); + ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges( + preferences.edit().putBoolean(PREFERENCE_EULA_ACCEPTED, true)); Intent startIntent = new Intent(activity, WelcomeActivity.class); activity.startActivityForResult(startIntent, MyTracksConstants.WELCOME); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java index ddf9306eb..c4ed31c11 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java @@ -41,6 +41,7 @@ import com.google.android.apps.mytracks.services.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.TrackRecordingService; import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.FileUtils; +import com.google.android.apps.mytracks.util.MyTracksUtils; import com.google.android.maps.mytracks.R; import android.app.Activity; @@ -251,6 +252,11 @@ public class MyTracks extends TabActivity implements OnTouchListener, Log.d(MyTracksConstants.TAG, "MyTracks.onCreate"); super.onCreate(savedInstanceState); instance = this; + ApiFeatures apiFeatures = ApiFeatures.getInstance(); + if (!MyTracksUtils.isRelease(this)) { + apiFeatures.getApiPlatformAdapter().enableStrictMode(); + } + providerUtils = MyTracksProviderUtils.Factory.get(this); menuManager = new MenuManager(this); sharedPreferences = getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); @@ -258,7 +264,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, // The volume we want to control is the Text-To-Speech volume int volumeStream = - new StatusAnnouncerFactory(ApiFeatures.getInstance()).getVolumeStream(); + new StatusAnnouncerFactory(apiFeatures).getVolumeStream(); setVolumeControlStream(volumeStream); // We don't need a window title bar: @@ -1058,10 +1064,10 @@ public class MyTracks extends TabActivity implements OnTouchListener, * @param trackId the id of the track */ public void setSelectedTrackId(final long trackId) { - sharedPreferences - .edit() - .putLong(getString(R.string.selected_track_key), trackId) - .commit(); + ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges( + sharedPreferences + .edit() + .putLong(getString(R.string.selected_track_key), trackId)); } long getSelectedTrackId() { diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java index 8a5f255f1..1e939b144 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksDeleteAllTracks.java @@ -16,6 +16,7 @@ package com.google.android.apps.mytracks; import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.maps.mytracks.R; import android.app.AlertDialog; @@ -63,7 +64,7 @@ public class MyTracksDeleteAllTracks extends Handler { context.getSharedPreferences(MyTracksSettings.SETTINGS_NAME, 0); SharedPreferences.Editor editor = prefs.edit(); editor.putLong(context.getString(R.string.selected_track_key), -1); - editor.commit(); + ApiFeatures.getInstance().getApiPlatformAdapter().applyPreferenceChanges(editor); if (done != null) { Handler h = new Handler(); h.post(done); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index 55147ebd3..f44823bce 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -644,7 +644,6 @@ public class TrackRecordingService extends Service implements LocationListener { locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); splitManager = new SplitManager(this); - sensorManager = SensorManagerFactory.getSensorManager(this); prefManager = new PreferenceManager(this); registerLocationListener(); @@ -731,7 +730,7 @@ public class TrackRecordingService extends Service implements LocationListener { splitManager.shutdown(); splitManager = null; if (sensorManager != null) { - sensorManager.onDestroy(); + sensorManager.shutdown(); sensorManager = null; } @@ -1080,6 +1079,7 @@ public class TrackRecordingService extends Service implements LocationListener { showNotification(); registerLocationListener(); splitManager.restore(); + sensorManager = SensorManagerFactory.getSensorManager(this); if (sensorManager != null) { sensorManager.onStartTrack(); } @@ -1128,6 +1128,7 @@ public class TrackRecordingService extends Service implements LocationListener { if (sensorManager != null) { sensorManager.shutdown(); + sensorManager = null; } releaseWakeLock(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java new file mode 100644 index 000000000..a26d0e8cd --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java @@ -0,0 +1,169 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors; + +import static com.google.android.apps.mytracks.MyTracksConstants.TAG; +import com.google.android.apps.mytracks.MyTracksSettings; +import com.google.android.apps.mytracks.content.Sensor; +import com.google.android.maps.mytracks.R; + +import com.dsi.ant.AntDefine; +import com.dsi.ant.AntMesg; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +/** + * A sensor manager to connect ANT+ sensors. + * This can include heart rate monitors. + * + * @author Sandor Dornbush + */ +public class AntDirectSensorManager extends AntSensorManager { + + /* + * These constants are defined by the ANT+ heart rate monitor spec. + */ + public static final byte HRM_CHANNEL = 0; + public static final byte NETWORK_NUMBER = 1; + public static final byte HEART_RATE_DEVICE_TYPE = 120; + public static final byte POWER_DEVICE_TYPE = 11; + public static final byte MANUFACTURER_ID = 1; + public static final short CHANNEL_PERIOD = 8070; + public static final byte RF_FREQUENCY = 57; + + private short deviceNumberHRM; + + public AntDirectSensorManager(Context context) { + super(context); + + deviceNumberHRM = WILDCARD; + + // First read the the device id that we will be pairing with. + SharedPreferences prefs = context.getSharedPreferences( + MyTracksSettings.SETTINGS_NAME, 0); + if (prefs != null) { + deviceNumberHRM = + (short) prefs.getInt(context.getString(R.string.ant_heart_rate_sensor_id_key), 0); + } + Log.i(TAG, "Pairing with heart rate monitor: " + deviceNumberHRM); + } + + @Override + public void handleMessage(byte[] antMessage) { + int channel = + antMessage[AntMesg.MESG_DATA_OFFSET] & AntDefine.CHANNEL_NUMBER_MASK; + switch (channel) { + case HRM_CHANNEL: + antDecodeHRM(antMessage); + break; + default: + Log.d(TAG, "Unhandled message: " + channel); + } + } + + /** + * Decode an ant heart rate monitor message. + * @param antMessage The byte array received from the heart rate monitor. + */ + private void antDecodeHRM(byte[] antMessage) { + // message ID == broadcast + switch (antMessage[AntMesg.MESG_ID_OFFSET]) { + case AntMesg.MESG_BROADCAST_DATA_ID: + handleBroadcastData(antMessage); + break; + case AntMesg.MESG_RESPONSE_EVENT_ID: + handleMessageResponse(antMessage); + break; + case AntMesg.MESG_CHANNEL_ID_ID: + handleChannelId(antMessage); + break; + default: + Log.e(TAG, "Unexpected message id: " + antMessage[3]); + } + } + + private void handleBroadcastData(byte[] antMessage) { + if (deviceNumberHRM == WILDCARD) { + getAntReceiver().ANTRequestMessage(HRM_CHANNEL, + AntMesg.MESG_CHANNEL_ID_ID); + Log.d(TAG, "Requesting channel id id."); + } + + setSensorState(Sensor.SensorState.CONNECTED); + int bpm = (int) antMessage[10] & 0xFF; + Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() + .setValue(bpm) + .setState(Sensor.SensorState.SENDING); + sensorData = + Sensor.SensorDataSet.newBuilder() + .setCreationTime(System.currentTimeMillis()) + .setHeartRate(b) + .build(); + } + + void handleChannelId(byte[] antMessage) { + // Store the device id. + deviceNumberHRM = + (short) (((int) antMessage[3] & 0xFF | + ((int) (antMessage[4] & 0xFF) << 8)) & 0xFFFF); + Log.i(TAG, "Found device id: " + deviceNumberHRM); + + SharedPreferences prefs = context.getSharedPreferences( + MyTracksSettings.SETTINGS_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(context.getString(R.string.ant_heart_rate_sensor_id_key), deviceNumberHRM); + editor.commit(); + } + + private void handleMessageResponse(byte[] antMessage) { + if (antMessage[3] == AntMesg.MESG_EVENT_ID && + antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { + // Search timeout + Log.w(TAG, "Search timed out. Unassigning channel."); + getAntReceiver().ANTUnassignChannel((byte) 0); + setSensorState(Sensor.SensorState.DISCONNECTED); + } else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) { + setSensorState(Sensor.SensorState.DISCONNECTED); + Log.i(TAG, "Disconnected from the sensor: " + getSensorState()); + } + } + + /** + * Open a channel to the SRM head unit. + */ + @Override + public void setupChannel() { + super.setupChannel(); + antChannelSetup(NETWORK_NUMBER, + HRM_CHANNEL, + deviceNumberHRM, + HEART_RATE_DEVICE_TYPE, + (byte) 0x01, + CHANNEL_PERIOD, + RF_FREQUENCY, + (byte) 0); + setSensorState(Sensor.SensorState.CONNECTING); + } + + public short getDeviceNumberHRM() { + return deviceNumberHRM; + } + + void setDeviceNumberHRM(short deviceNumberHRM) { + this.deviceNumberHRM = deviceNumberHRM; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java new file mode 100644 index 000000000..3d038a922 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java @@ -0,0 +1,226 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors; + +import com.google.android.apps.mytracks.MyTracksConstants; +import com.google.android.apps.mytracks.MyTracksSettings; +import com.google.android.apps.mytracks.content.Sensor; +import com.google.android.maps.mytracks.R; + +import com.dsi.ant.AntDefine; +import com.dsi.ant.AntMesg; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +/** + * A sensor manager that can connect to an SRM Power Control 7 head unit. + * + * @author Sandor Dornbush + */ +public class AntSRMSensorManager extends AntSensorManager { + + /* + * These constants are defined by the SRM Headunit spec. + */ + public static final byte NETWORK_NUMBER = (byte) 0; + public static final byte DEVICE_TYPE = (byte) 12; + public static final byte MANUFACTURER_ID = (byte) 1; + public static final short CHANNEL_PERIOD = (short) 8192; + public static final byte RF_FREQUENCY = (byte) 50; + + public static final int MESSAGE_TYPE_INDEX = 3; + public static final int INITIALS_MESSAGE_TYPE = 5; + public static final int SENSOR_DATA_MESSAGE_TYPE = 6; + + public static final int MESSAGE_ID_INDEX = 4; + + /** + * This can be any value between 0..8 + */ + private byte channel = (byte) 5; + + /** + * This is the id of this device. + * The head unit must be programmed to expect this number. + */ + private byte deviceId; + + /** + * The id of the last message received from the SRM head unit. + */ + private int lastMessageId; + + public AntSRMSensorManager(Context context) { + super(context); + + // First read the the device id that we will be announcing. + SharedPreferences prefs = context.getSharedPreferences( + MyTracksSettings.SETTINGS_NAME, 0); + if (prefs != null) { + deviceId = (byte) prefs.getInt( + context.getString(R.string.ant_srm_bridge_sensor_id_key), + WILDCARD); + } + } + + @Override + public void handleMessage(byte[] antMessage) { + // Parse channel number + byte recievedChannel = (byte) (antMessage[AntMesg.MESG_DATA_OFFSET] + & AntDefine.CHANNEL_NUMBER_MASK); + if (recievedChannel != channel) { + Log.d(MyTracksConstants.TAG, "Unexpected channel: " + recievedChannel); + return; + } + switch (antMessage[AntMesg.MESG_ID_OFFSET]) { + case AntMesg.MESG_BROADCAST_DATA_ID: + handleBroadcastData(antMessage); + break; + case AntMesg.MESG_RESPONSE_EVENT_ID: + handleMessageResponse(antMessage); + break; + case AntMesg.MESG_CHANNEL_ID_ID: + handleChannelId(antMessage); + break; + default: + Log.e(MyTracksConstants.TAG, + "Unexpected message id: " + antMessage[3]); + } + } + + private void handleBroadcastData(byte[] antMessage) { + if (deviceId == WILDCARD) { + getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID); + Log.d(MyTracksConstants.TAG, "Requesting channel id id."); + } + setSensorState(Sensor.SensorState.CONNECTED); + switch(antMessage[MESSAGE_TYPE_INDEX]) { + case INITIALS_MESSAGE_TYPE: + // TODO handle initials message. + break; + case SENSOR_DATA_MESSAGE_TYPE: + parseSensorData(antMessage); + break; + default: + Log.e(MyTracksConstants.TAG, + "Unexpected message type: " + antMessage[MESSAGE_TYPE_INDEX]); + } + } + + private void handleChannelId(byte[] antMessage) { + // Store the device id. + deviceId = antMessage[3]; + Log.i(MyTracksConstants.TAG, "Found device id: " + deviceId); + + SharedPreferences prefs = context.getSharedPreferences( + MyTracksSettings.SETTINGS_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(context.getString(R.string.ant_srm_bridge_sensor_id_key), deviceId); + editor.commit(); + } + + private void handleMessageResponse(byte[] antMessage) { + if (antMessage[3] == AntMesg.MESG_EVENT_ID + && antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { + // Search timeout + Log.w(MyTracksConstants.TAG, "Search timed out. Unassigning channel."); + getAntReceiver().ANTUnassignChannel(channel); + setSensorState(Sensor.SensorState.DISCONNECTED); + } else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) { + setSensorState(Sensor.SensorState.DISCONNECTED); + Log.i(MyTracksConstants.TAG, + "Disconnected from the sensor: " + getSensorState()); + } + } + + private void parseSensorData(byte[] antMessage) { + if (antMessage.length != 11) { + Log.e(MyTracksConstants.TAG, + "Unexpected ant message length: " + antMessage.length); + return; + } + + int newMessageId = antMessage[MESSAGE_ID_INDEX] & 0xFF; + if (lastMessageId == newMessageId) { + // Repeated message. + Log.i(MyTracksConstants.TAG, + String.format("SRM ignoring repeat: 0x%X", newMessageId)); + return; + } + if (newMessageId < lastMessageId) { + if (!(newMessageId < 20 && lastMessageId > 200)) { + Log.i(MyTracksConstants.TAG, + String.format("SRM ignoring repeat: 0x%X", newMessageId)); + return; + } // else assume the byte overflowed to 0. + } + lastMessageId = newMessageId; + + Sensor.SensorDataSet.Builder builder = Sensor.SensorDataSet.newBuilder() + .setCreationTime(System.currentTimeMillis()); + + int power = SensorUtils.unsignedShortToInt(antMessage, 5); + if (power >= 0) { + Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() + .setValue(power) + .setState(Sensor.SensorState.SENDING); + builder.setPower(b); + } + + int speed = SensorUtils.unsignedShortToInt(antMessage, 7); + if (speed >= 0) { + // The speed from srm is in 1/10 km/hr. + // Sensor data expects the speed as m/s. + // TODO finish this. + //sensorData.setWheelSpeed(UnitConversions.TENTH_KMH_TO_MPS * speed); + } + + int cadence = antMessage[9] & 0xFF; + if (cadence >= 0) { + Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() + .setValue(cadence) + .setState(Sensor.SensorState.SENDING); + builder.setCadence(b); + } + + int bpm = antMessage[10] & 0xFF; + if (bpm > 0) { + Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() + .setValue(bpm) + .setState(Sensor.SensorState.SENDING); + builder.setHeartRate(b); + } + sensorData = builder.build(); + } + + /** + * Open a channel to the SRM head unit. + */ + @Override + public void setupChannel() { + antChannelSetup(NETWORK_NUMBER, + channel, + deviceId, + DEVICE_TYPE, + MANUFACTURER_ID, + CHANNEL_PERIOD, + RF_FREQUENCY, + (byte) 0); + lastMessageId = 0; + setSensorState(Sensor.SensorState.CONNECTING); + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java new file mode 100644 index 000000000..eb2230f10 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java @@ -0,0 +1,228 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors; + +import com.google.android.apps.mytracks.MyTracksConstants; +import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; + +import com.dsi.ant.AntDefine; +import com.dsi.ant.AntInterface; +import com.dsi.ant.AntInterfaceIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +/** + * An abstract class for ANT+ based sensors. + * + * @author Sandor Dornbush + */ +public abstract class AntSensorManager extends SensorManager { + + // ANT Service + public static final int EVENT_ANT_ENABLED = 1; + public static final int EVENT_ANT_DISABLED = 2; + + // Pairing + protected static final short WILDCARD = 0; + + private AntInterface antReceiver; + + private boolean status; + + // Flag to know if the ANT App was interrupted + // TODO: This code path is not used but probably should be. + private boolean antInterrupted; + + /** + * The data from the sensors. + */ + protected SensorDataSet sensorData; + + protected Context context; + + private static final boolean DEBUGGING = false; + + /** + * Receives all of the ANT intents and dispatches to the proper handler. + */ + private final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String antAction = intent.getAction(); + + Log.i(MyTracksConstants.TAG, "enter onReceive" + antAction); + if (antAction.equals(AntInterfaceIntent.ANT_ENABLED_ACTION)) { + Log.i(MyTracksConstants.TAG, "enter onReceive ANT_ENABLED_ACTION " + + antAction); + handler.sendMessage(handler.obtainMessage(EVENT_ANT_ENABLED, 0)); + } + if (antAction.equals(AntInterfaceIntent.ANT_DISABLED_ACTION)) { + Log.i(MyTracksConstants.TAG, "enter onReceive ANT_DISABLED_ACTION " + + antAction); + handler.sendMessage(handler.obtainMessage(EVENT_ANT_DISABLED, 0)); + } + + if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) { + byte[] antMessage = + intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE); + if (DEBUGGING) { + StringBuilder sb = + new StringBuilder("ANT_RX_MESSAGE_ACTION Rx:"); + + for (int i = 0; i < antMessage.length; i++) + sb.append(String.format("[%X]]", (antMessage[i] & 0xFF))); + + Log.d(MyTracksConstants.TAG, sb.toString()); + } + + handleMessage(antMessage); + } + } + }; + + /** + * Handler for all the ANT related events. The events will be handled only + * when we are in the ANT application main menu + **/ + private Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_ANT_ENABLED: + Log.i(MyTracksConstants.TAG, + "enter handleMessage ----EVENT_ANT_ENABLED"); + break; + case EVENT_ANT_DISABLED: + Log.i(MyTracksConstants.TAG, + "enter handleMessage ----EVENT_ANT_DISABLED"); + break; + } + } + }; + + public AntSensorManager(Context context) { + this.context = context; + + // Need to enable the ANT if it is not enabled earlier + antReceiver = AntInterface.getInstance(context, null); + if (antReceiver == null) { + return; + } + + status = antReceiver.isEnabled(); + if (!status) { + // Make sure not to call AntInterface.enable() again, if it has been + // already called before + if (antInterrupted == false) { + status = antReceiver.enable(); + if (!status) { + Log.e(MyTracksConstants.TAG, "Can not enable ANT interface"); + } else { + Log.i(MyTracksConstants.TAG, "Powering on Radio"); + } + } else { + Log.i(MyTracksConstants.TAG, "Radio already enabled"); + } + } + } + + @Override + public void setupChannel() { + Log.i(MyTracksConstants.TAG, "Registering for ant intents."); + // Register for ANT intent broadcasts. + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION); + intentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION); + intentFilter.addAction(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION); + context.registerReceiver(receiver, intentFilter); + } + + @Override + public SensorDataSet getSensorDataSet() { + return sensorData; + } + + @Override + public boolean isEnabled() { + return true; + } + + public abstract void handleMessage(byte[] antMessage); + + @Override + public void onDestroy() { + Log.i(MyTracksConstants.TAG, "Unregistering for ant intents."); + context.unregisterReceiver(receiver); + } + + /** + * Set up an ANT+ channel. + * + * @return True on success. + */ + protected boolean antChannelSetup(byte networkNumber, byte channelNumber, + short deviceNumber, byte deviceType, byte txType, short channelPeriod, + byte radioFreq, byte proxSearch) { + + // Assign as slave channel on selected network (0 = public, 1 = ANT+, 2 = + // ANTFS) + if (!antReceiver.ANTAssignChannel(channelNumber, + AntDefine.PARAMETER_RX_NOT_TX, networkNumber)) { + return false; + } + + if (!antReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, + txType)) { + return false; + } + + if (!antReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod)) { + return false; + } + + if (!antReceiver.ANTSetChannelRFFreq(channelNumber, radioFreq)) { + return false; + } + + // Disable high priority search + if (!antReceiver.ANTSetChannelSearchTimeout(channelNumber, (byte) 0)) { + return false; + } + + // Set search timeout to 30 seconds (low priority search)) + if (!antReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12)) { + return false; + } + + if (deviceNumber == WILDCARD) { + // Configure proximity search, if using wild card search + if (!antReceiver.ANTSetProximitySearch(channelNumber, proxSearch)) { + return false; + } + } + + return !antReceiver.ANTOpenChannel(channelNumber); + } + + public AntInterface getAntReceiver() { + return antReceiver; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java index 01a8d8bf5..cad3e707b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactory.java @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package com.google.android.apps.mytracks.services.sensors; import com.google.android.apps.mytracks.MyTracksConstants; @@ -46,15 +45,19 @@ public class SensorManagerFactory { return null; } - String sensor = prefs.getString(context.getString(R.string.sensor_type_key), - null); + String sensor = prefs.getString(context.getString(R.string.sensor_type_key), null); Log.i(MyTracksConstants.TAG, "Creating sensor of type: " + sensor); if (sensor == null) { return null; + } else if (sensor.equals(context.getString(R.string.ant_sensor_type))) { + return new AntDirectSensorManager(context); + } else if (sensor.equals(context.getString(R.string.srm_ant_bridge_sensor_type))) { + return new AntSRMSensorManager(context); } else if (sensor.equals(context.getString(R.string.zephyr_sensor_type))) { return new ZephyrSensorManager(context); } else { + Log.w(MyTracksConstants.TAG, "Unable to find sensor type: " + sensor); return null; } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java index 8cf8f951f..3a2fbf9e4 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java @@ -35,8 +35,9 @@ public class ZephyrMessageParser implements MessageParser { } Log.w(MyTracksConstants.TAG, "Got zephyr data: " + sb); // The provided units are 1/16 strides per minute. - int cadence = SensorUtils.unsignedShortToInt(buffer, 56); - // Heart Rate + // TODO: Fix the cadence calculation. + // int cadence = SensorUtils.unsignedShortToInt(buffer, 56); + // Heart Rate Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() .setValue(buffer[12] & 0xFF) .setState(Sensor.SensorState.SENDING); diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java index 06727cd2d..7f1016856 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java @@ -62,24 +62,18 @@ public class ApiFeatures { * Allow subclasses for mocking, but no direct instantiation. */ protected ApiFeatures() { - if (getApiLevel() >= 5) { - try { - Class clazz = Class.forName( - "com.google.android.apps.mytracks.util.EclairPlatformAdapter"); - apiPlatformAdapter = (ApiPlatformAdapter) clazz.newInstance(); - } catch (Exception e) { - Log.i(MyTracksConstants.TAG, "ApiFeatures: Unable to instantiate Eclair" - + " platform adapter", e); - } - } - if (apiPlatformAdapter == null) { + if (getApiLevel() >= 9) { + apiPlatformAdapter = new GingerbreadPlatformAdapter(); + } else if (getApiLevel() >= 5) { + apiPlatformAdapter = new EclairPlatformAdapter(); + } else { Log.i(MyTracksConstants.TAG, "ApiFeatures: Using default platform adapter"); // Cupcake adapter is always supported, so it's safe to do static linkage. apiPlatformAdapter = new CupcakePlatformAdapter(); } } - + public ApiPlatformAdapter getApiPlatformAdapter() { return apiPlatformAdapter; } @@ -107,7 +101,11 @@ public class ApiFeatures { return true; } - + + public boolean hasStrictMode() { + return getApiLevel() >= 9; + } + // Visible for testing. protected int getApiLevel() { return ANDROID_API_LEVEL; diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiPlatformAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiPlatformAdapter.java index 1e2a5210a..c68832735 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiPlatformAdapter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiPlatformAdapter.java @@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; +import android.content.SharedPreferences; import android.os.HandlerThread; /** @@ -57,4 +58,15 @@ public interface ApiPlatformAdapter { * @return true whether the handler has been stopped or not. */ boolean stopHandlerThread(HandlerThread handlerThread); + + /** + * Applies all changes done to the given preferences editor. + * Changes may or may not be applied immediately. + */ + void applyPreferenceChanges(SharedPreferences.Editor editor); + + /** + * Enables strict mode where supported, only if this is a development build. + */ + void enableStrictMode(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/CupcakePlatformAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/CupcakePlatformAdapter.java index 272b47218..354f0a307 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/CupcakePlatformAdapter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/CupcakePlatformAdapter.java @@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; +import android.content.SharedPreferences.Editor; import android.os.HandlerThread; /** @@ -50,4 +51,14 @@ public class CupcakePlatformAdapter implements ApiPlatformAdapter { // Do nothing, as Cupcake doesn't provide quit(). return false; } + + @Override + public void applyPreferenceChanges(Editor editor) { + editor.commit(); + } + + @Override + public void enableStrictMode() { + // Not supported + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/EclairPlatformAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/EclairPlatformAdapter.java index 9c32946d3..91c1ea1d3 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/EclairPlatformAdapter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/EclairPlatformAdapter.java @@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; +import android.content.SharedPreferences.Editor; import android.os.HandlerThread; /** @@ -45,4 +46,14 @@ public class EclairPlatformAdapter implements ApiPlatformAdapter { public boolean stopHandlerThread(HandlerThread handlerThread) { return handlerThread.quit(); } + + @Override + public void applyPreferenceChanges(Editor editor) { + editor.commit(); + } + + @Override + public void enableStrictMode() { + // Not supported + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/GingerbreadPlatformAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/GingerbreadPlatformAdapter.java new file mode 100644 index 000000000..caa73a170 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/util/GingerbreadPlatformAdapter.java @@ -0,0 +1,36 @@ +package com.google.android.apps.mytracks.util; + +import com.google.android.apps.mytracks.MyTracksConstants; + +import android.content.SharedPreferences.Editor; +import android.os.StrictMode; +import android.util.Log; + +/** + * The Gingerbread (API level 9) specific implememntation of the + * {@link ApiPlatformAdapter}. + * + * @author Rodrigo Damazio + */ +public class GingerbreadPlatformAdapter extends EclairPlatformAdapter { + @Override + public void applyPreferenceChanges(Editor editor) { + // Apply asynchronously + editor.apply(); + } + + @Override + public void enableStrictMode() { + Log.d(MyTracksConstants.TAG, "Enabling strict mode"); + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() + .detectDiskWrites() + .detectNetwork() + .penaltyLog() + .build()); + StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() + .detectAll() + .penaltyLog() + .penaltyDeath() + .build()); + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java index 71caa003c..52f5dee72 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/MyTracksUtils.java @@ -21,7 +21,12 @@ import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.maps.GeoPoint; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; import android.location.Location; +import android.os.StrictMode; import android.util.Log; import java.util.ArrayList; @@ -34,6 +39,7 @@ import java.util.Stack; * @author Leif Hendrik Wilden */ public class MyTracksUtils { + private static final int RELEASE_SIGNATURE_HASHCODE = -1855564782; /** * Computes the distance on the two sphere between the point c0 and the line @@ -246,6 +252,25 @@ public class MyTracksUtils { (int) (location.getLongitude() * 1E6)); } + /** + * Returns whether or not this is a release build. + */ + public static boolean isRelease(Context context) { + try { + Signature [] sigs = context.getPackageManager().getPackageInfo( + context.getPackageName(), PackageManager.GET_SIGNATURES).signatures; + for (Signature sig : sigs) { + if (sig.hashCode() == RELEASE_SIGNATURE_HASHCODE) { + return true; + } + } + } catch (NameNotFoundException e) { + Log.e(MyTracksConstants.TAG, "Unable to get signatures", e); + } + + return false; + } + /** * This is a utility class w/ only static memebers. */ diff --git a/MyTracksLib/default.properties b/MyTracksLib/default.properties index 948be06ed..1694c805b 100644 --- a/MyTracksLib/default.properties +++ b/MyTracksLib/default.properties @@ -8,5 +8,5 @@ # project structure. # Project target. -target=android-8 +target=android-9 android.library=true diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java index 5d134effb..4e90d7227 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java @@ -861,18 +861,22 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { String select = TracksColumns._ID + "=" + id; return findTrackBy(select); } - + @Override public List getAllTracks() { Cursor cursor = getTracksCursor(null); - if (cursor == null || !cursor.moveToFirst()) { - return new ArrayList(); - } + ArrayList tracks = new ArrayList(); + if (cursor != null) { + tracks.ensureCapacity(cursor.getCount()); - List tracks = new ArrayList(cursor.getCount()); - do { - tracks.add(createTrack(cursor)); - } while(cursor.moveToNext()); + if (cursor.moveToFirst()) { + do { + tracks.add(createTrack(cursor)); + } while(cursor.moveToNext()); + } + + cursor.close(); + } return tracks; } diff --git a/MyTracksTest/default.properties b/MyTracksTest/default.properties index c4fb0851b..85cbfc4dd 100644 --- a/MyTracksTest/default.properties +++ b/MyTracksTest/default.properties @@ -10,4 +10,4 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=Google Inc.:Google APIs:8 +target=Google Inc.:Google APIs:9 diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelperTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelperTest.java index 0cdd7c626..e2c8b3b48 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelperTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelperTest.java @@ -38,7 +38,7 @@ public class PreferenceBackupHelperTest extends TestCase { */ private class MockPreferenceEditor implements SharedPreferences.Editor { private Map newPreferences = new HashMap(preferenceValues); - + @Override public Editor clear() { newPreferences.clear(); @@ -51,6 +51,11 @@ public class PreferenceBackupHelperTest extends TestCase { return true; } + @Override + public void apply() { + commit(); + } + @Override public Editor putBoolean(String key, boolean value) { return put(key, value); diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java index 9f3e0e186..db0236aa2 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java @@ -502,7 +502,7 @@ public class TrackRecordingServiceTest public void testDeleteAllTracks_trackInProgress() throws Exception { createDummyTrack(123, -1, true); assertEquals(1, providerUtils.getAllTracks().size()); - + ITrackRecordingService service = bindAndGetService(createStartIntent()); assertTrue(service.isRecording()); diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java new file mode 100644 index 000000000..22c2f0fa1 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors; + +import com.dsi.ant.AntMesg; +import com.google.android.apps.mytracks.MyTracksSettings; +import com.google.android.apps.mytracks.content.Sensor; +import com.google.android.maps.mytracks.R; + +import android.content.SharedPreferences; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +public class AntDirectSensorManagerTest extends AndroidTestCase { + + private SharedPreferences sharedPreferences; + private AntDirectSensorManager manager; + + public void setUp() { + sharedPreferences = getContext().getSharedPreferences( + MyTracksSettings.SETTINGS_NAME, 0); + // Let's use default values. + sharedPreferences.edit().clear().commit(); + manager = new AntDirectSensorManager(getContext()); + } + + @SmallTest + public void testBroadcastData() { + manager.setDeviceNumberHRM((short) 42); + byte[] buff = new byte[11]; + buff[1] = AntMesg.MESG_BROADCAST_DATA_ID; + buff[2] = 0; // HRM CHANNEL + buff[10] = (byte) 220; + manager.handleMessage(buff); + + Sensor.SensorDataSet sds = manager.getSensorDataSet(); + assertNotNull(sds); + assertTrue(sds.hasHeartRate()); + assertEquals(Sensor.SensorState.SENDING, + sds.getHeartRate().getState()); + assertEquals(220, sds.getHeartRate().getValue()); + + assertFalse(sds.hasCadence()); + assertFalse(sds.hasPower()); + } + + @SmallTest + public void testHandleChannelId() { + assertEquals(0, manager.getDeviceNumberHRM()); + short id = 42; + byte[] buff = new byte[5]; + buff[3] = 42; + manager.handleChannelId(buff); + assertEquals(id, manager.getDeviceNumberHRM()); + } + + @SmallTest + public void testChannelId() { + byte[] buff = new byte[11]; + buff[1] = AntMesg.MESG_CHANNEL_ID_ID; + buff[3] = 42; + manager.handleMessage(buff); + + assertEquals(42, manager.getDeviceNumberHRM()); + assertEquals(42, + sharedPreferences.getInt( + getContext().getString(R.string.ant_heart_rate_sensor_id_key), -1)); + assertNull(manager.getSensorDataSet()); + } + + @SmallTest + public void testResponseEvent() { + assertEquals(Sensor.SensorState.NONE, manager.getSensorState()); + byte[] buff = new byte[11]; + buff[1] = AntMesg.MESG_RESPONSE_EVENT_ID; + buff[3] = AntMesg.MESG_UNASSIGN_CHANNEL_ID; + manager.handleMessage(buff); + + assertEquals(Sensor.SensorState.DISCONNECTED, manager.getSensorState()); + assertNull(manager.getSensorDataSet()); + } + + // TODO: Test timeout too. +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java index 4dcc0b9d9..3951aa3c1 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/SensorManagerFactoryTest.java @@ -28,12 +28,26 @@ public class SensorManagerFactoryTest extends AndroidTestCase { @SmallTest public void testCreateZephyr() throws Exception { + assertClassForName(ZephyrSensorManager.class, R.string.zephyr_sensor_type); + } + + @SmallTest + public void testCreateAnt() throws Exception { + assertClassForName(AntDirectSensorManager.class, R.string.ant_sensor_type); + } + + @SmallTest + public void testCreateAntSRM() throws Exception { + assertClassForName(AntSRMSensorManager.class, R.string.srm_ant_bridge_sensor_type); + } + + private void assertClassForName(Class c, int i) { sharedPreferences.edit() - .putString(getContext().getString(R.string.sensor_type_key), - getContext().getString(R.string.zephyr_sensor_type)) - .commit(); + .putString(getContext().getString(R.string.sensor_type_key), + getContext().getString(i)) + .commit(); SensorManager sm = SensorManagerFactory.getSensorManager(getContext()); assertNotNull(sm); - assertTrue(sm instanceof ZephyrSensorManager); + assertTrue(c.isInstance(sm)); } } diff --git a/SignalStrengthSampler/AndroidManifest.xml b/SignalStrengthSampler/AndroidManifest.xml index 03d216a34..9ede9ed88 100644 --- a/SignalStrengthSampler/AndroidManifest.xml +++ b/SignalStrengthSampler/AndroidManifest.xml @@ -54,7 +54,8 @@ limitations under the License. - diff --git a/SignalStrengthSampler/default.properties b/SignalStrengthSampler/default.properties index 45aae789b..903536e41 100644 --- a/SignalStrengthSampler/default.properties +++ b/SignalStrengthSampler/default.properties @@ -8,5 +8,6 @@ # project structure. # Project target. -target=Google Inc.:Google APIs:8 +target=Google Inc.:Google APIs:9 +proguard.config=proguard.cfg android.library.reference.1=../MyTracksLib/ diff --git a/SignalStrengthSamplerTest/default.properties b/SignalStrengthSamplerTest/default.properties index 420db56e3..77f7ad75b 100644 --- a/SignalStrengthSamplerTest/default.properties +++ b/SignalStrengthSamplerTest/default.properties @@ -8,4 +8,4 @@ # project structure. # Project target. -target=Google Inc.:Google APIs:8 +target=Google Inc.:Google APIs:9