diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml
index 3598aec25..6f3b16692 100755
--- a/MyTracks/AndroidManifest.xml
+++ b/MyTracks/AndroidManifest.xml
@@ -119,6 +119,9 @@ limitations under the License.
+
+
+
diff --git a/MyTracks/res/values-de/arrays.xml b/MyTracks/res/values-de/arrays.xml
index 8666f77c5..ed7915fc8 100644
--- a/MyTracks/res/values-de/arrays.xml
+++ b/MyTracks/res/values-de/arrays.xml
@@ -17,7 +17,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 9514ddec3..a17e5ac0a 100644
--- a/MyTracks/res/values-de/strings.xml
+++ b/MyTracks/res/values-de/strings.xml
@@ -26,52 +26,54 @@ limitations under the License.
Alle Tracks löschen
Einstellungen
Höhe
- Bitte wählen Sie einen Track zur Ansicht aus
+ Bitte wähle einen Track zur Ansicht aus
Aktueller Track
Letztes Segment
+ Sensorstatus
+ Aggregierte Statistiken
- 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 unmöglich. Bitte versuchen Sie es nochmal.
- Datei nicht lesbar.
+ 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 versuchen Sie es nochmal.
+ Ein Fehler ist aufgetreten. Bitte versuche es noch einmal.
Track existiert nicht.
- Die Datei ist zu groß (Obergrenze sind 500 KB). Operation abgebrochen.
+ Die Datei ist zu groß (maximal 500 KB). 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.
+ Warte auf ein besseres GPS-Signal. Bitte stelle sicher, dass der Himmel sichtbar ist.
Ein neuer Track wird aufgezeichnet.
- Die Aufzeichnung wurde beendet.
+ Die Aufzeichnung wurde angehalten.
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.
+ 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.
- 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.
+ 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…
- 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 dauert ein wenig.
- Schreibe Datei auf SD-Karte. Das dauert ein wenig.
- Aktualisiere Trackstatistik
+ 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?
@@ -80,17 +82,19 @@ limitations under the License.
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)"
+ Aktivität
+ Art des Wegpunktes
+ (Teil %d)
- "Ihr Track wird aufgezeichnet…"
+ Dein Track wird aufgezeichnet…
Nein
@@ -100,7 +104,7 @@ limitations under the License.
Wegpunkt
- (Anfang)
+ (Beginn)
(Ende)
@@ -114,15 +118,22 @@ limitations under the License.
Auf SD-Karte speichern…
GPX-Datei exportieren
KML-Datei exportieren
+ CSV-Datei exportieren
+ TCX-Datei exportieren
An Google MyMaps senden
Alle Tracks löschen
- Eine Track auswählen. Lange berühren für mehr Optionen.
- Alle Tracks:
+
+ 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.
@@ -150,64 +161,99 @@ limitations under the License.
Einstellungen für My Tracks
Anzeige
- Aufzeichnen
- Mit Freunden Teilen
+ Aufzeichnung
+ Mit Freunden teilen
- Dauer zwischen Ansagen:
- Kürzeste Zeit zwichen zwei Ansagen
+ In dieser Android-Version nicht verfügbar
+
+ Dauer zwischen Ansagen
+ Kürzeste Zeit zwischen zwei Ansagen
- Kleinste Entfernung zwischen Punkten:
+ 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.
+ 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.
+ 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.
+ 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 britischen Längeneinheiten 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:
+ Geschwindigkeitsanzeige
Geschwindigkeit wird angezeigt.
- Gangart 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.
+ 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.
- Tempo
+ 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:
+ Wähle eine Karte von Deinem Konto aus
Neue Karte erstellen…
An Google My Maps senden
Karten werden geladen…
- Ihre Karten konnten im Augenblick nicht geladen werden!
+ 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 können Sie trotzdem von anderen durch einen direkten Link angezeigt werden.
+ 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
@@ -222,6 +268,8 @@ limitations under the License.
Vielleicht interessierst Du Dich für diese Track:
%s.
+
+
Öffentlich
Nicht in Suchergebnissen enthalten.
Du hast noch keine Karten.
@@ -229,11 +277,11 @@ limitations under the License.
Durchschnitt in Bew.
- Durchschnitt in Bew.
+ Pace in Bew.
Durchschnittsgeschw.
- Durchschnittstempo
+ Durchschnitts-Pace
Höhenprofil
Höhengewinn
@@ -244,7 +292,7 @@ limitations under the License.
Niedrigste Höhenlage
Minimale Steigung
- Schnellstes Tempo
+ Minimaler Pace
Höchste Höhenlage
@@ -254,14 +302,14 @@ limitations under the License.
Lade Track…
- Diagram Einstellungen
+ Diagramm-Einstellungen
- X Achse
+ X-Achse
Nach Entfernung
Nach Zeit
- Y Achse
+ Y-Achse
@@ -282,7 +330,7 @@ limitations under the License.
Kilometer pro Stunde
pro Kilometer
Meilen pro Stunde
- pro meile
+ pro Meile
Zeit in Bewegung
@@ -290,10 +338,14 @@ limitations under the License.
Zeit
SD-Karte nicht verfügbar
Geschwindigkeit
- Sie müssen erst eine Strecke zurücklegen.
+ Du musst erst eine Aufzeichnung beginnen.
Statistik
Gesamtentfernung
Entfernung
+ Energie
+ Kadenz
+ Herzfrequenz
+ Höchste Herzfrequenz
Bitte warten…
@@ -316,22 +368,24 @@ limitations under the License.
Kein Konto vorhanden
- Kein Konto gefunden um Tracks zu versenden.
- Wählen Sei ein Konto
+ Kein Konto gefunden, um Tracks zu versenden.
+ Wähle 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.
+ 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
- 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.
+ 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
@@ -348,4 +402,40 @@ limitations under the License.
$6 : Speed units (mph) : Pace units (per mile)
-->
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 c2d8886e9..487afee53 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)
SD Card Backup
@@ -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/ExportAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java
index 3a276f86e..1eb86831a 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java
@@ -46,6 +46,7 @@ public class ExportAllTracks {
public static final int GPX_OPTION_INDEX = 0;
public static final int KML_OPTION_INDEX = 1;
public static final int CSV_OPTION_INDEX = 2;
+ public static final int TCX_OPTION_INDEX = 3;
private final Activity activity;
private WakeLock wakeLock;
@@ -67,6 +68,11 @@ public class ExportAllTracks {
case CSV_OPTION_INDEX:
format = TrackFileFormat.CSV;
break;
+ case TCX_OPTION_INDEX:
+ format = TrackFileFormat.TCX;
+ break;
+ default:
+ Log.w(MyTracksConstants.TAG, "Unknown export format: " + which);
}
}
};
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/MyTracksTest/src/com/google/android/apps/mytracks/io/TrackWriterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/TrackWriterTest.java
index 12542199b..73721214e 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/io/TrackWriterTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/TrackWriterTest.java
@@ -2,31 +2,27 @@
package com.google.android.apps.mytracks.io;
-import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.isA;
-import static org.easymock.EasyMock.leq;
-import static org.easymock.EasyMock.same;
-import com.google.android.apps.mytracks.MyTracksConstants;
+import com.google.android.apps.mytracks.content.MyTracksProvider;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
-import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
+import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
+import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import android.content.Context;
-import android.database.MatrixCursor;
import android.location.Location;
-import android.provider.BaseColumns;
import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.test.mock.MockContentResolver;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import org.easymock.EasyMock;
-import org.easymock.IAnswer;
import org.easymock.IArgumentMatcher;
import org.easymock.IMocksControl;
@@ -138,12 +134,19 @@ public class TrackWriterTest extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
+ MockContentResolver mockContentResolver = new MockContentResolver();
+ RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
+ getContext(), getContext(), "test.");
+ Context context = new MockContext(mockContentResolver, targetContext);
+ MyTracksProvider provider = new MyTracksProvider();
+ provider.attachInfo(context, null);
+ mockContentResolver.addProvider(MyTracksProviderUtils.AUTHORITY, provider);
+ setContext(context);
+ providerUtils = MyTracksProviderUtils.Factory.get(context);
+ oldProviderUtilsFactory = TestingProviderUtilsFactory.installWithInstance(providerUtils);
+
mocksControl = EasyMock.createStrictControl();
formatWriter = mocksControl.createMock(TrackFormatWriter.class);
- providerUtils = mocksControl.createMock(MyTracksProviderUtils.class);
- oldProviderUtilsFactory =
- TestingProviderUtilsFactory.installWithInstance(providerUtils);
-
expect(formatWriter.getExtension()).andStubReturn(EXTENSION);
track = new Track();
@@ -225,13 +228,6 @@ public class TrackWriterTest extends AndroidTestCase {
public void testWriteDocument_emptyTrack() {
writer = new TrackWriter(getContext(), providerUtils, track, formatWriter);
- // Don't let it write any waypoints
- expect(providerUtils.getWaypointsCursor(
- TRACK_ID, 0, MyTracksConstants.MAX_LOADED_WAYPOINTS_POINTS))
- .andStubReturn(null);
- expect(providerUtils.getLocationsCursor(
- eq(TRACK_ID), leq(0L), leq(0), eq(false))).andStubReturn(null);
-
// Set expected mock behavior
formatWriter.writeHeader();
formatWriter.writeFooter();
@@ -253,7 +249,7 @@ public class TrackWriterTest extends AndroidTestCase {
new Location("fake2"),
new Location("fake3"),
new Location("fake4"),
- new Location("fake5"),
+ new Location("fake5")
};
Waypoint[] wps = { new Waypoint(), new Waypoint(), new Waypoint() };
@@ -262,37 +258,14 @@ public class TrackWriterTest extends AndroidTestCase {
// Make location 3 invalid
locs[2].setLatitude(100);
-
- // Set up cursors
- // We use fake columns since the cursor is only read by the provider utils
- final MatrixCursor locCursor =
- new MatrixCursor(new String[] { BaseColumns._ID }, 6);
- for (int i = 1; i <= 6; i++) {
- locCursor.newRow().add(i);
+
+ assertEquals(locs.length, providerUtils.bulkInsertTrackPoints(locs, locs.length, TRACK_ID));
+ for (int i = 0; i < wps.length; ++i) {
+ Waypoint wpt = wps[i];
+ wpt.setTrackId(TRACK_ID);
+ assertNotNull(providerUtils.insertWaypoint(wpt));
+ wpt.setId(i + 1);
}
- expect(providerUtils.getLocationsCursor(
- eq(TRACK_ID), leq(0L), leq(0), eq(false))).andStubReturn(locCursor);
- providerUtils.fillLocation(same(locCursor), isA(Location.class));
- EasyMock.expectLastCall().andStubAnswer(new IAnswer() {
- @Override
- public Void answer() throws Throwable {
- Location loc = (Location) EasyMock.getCurrentArguments()[1];
- loc.set(locs[locCursor.getPosition()]);
- return null;
- }
- });
-
- MatrixCursor wpCursor =
- new MatrixCursor(new String[] { BaseColumns._ID }, 3);
- wpCursor.newRow().add(1);
- wpCursor.newRow().add(2);
- wpCursor.newRow().add(3);
- expect(providerUtils.getWaypointsCursor(
- eq(TRACK_ID), leq(0L),
- eq(MyTracksConstants.MAX_LOADED_WAYPOINTS_POINTS)))
- .andStubReturn(wpCursor);
- expect(providerUtils.createWaypoint(wpCursor))
- .andStubAnswer(stubCursorToArray(wpCursor, wps));
// Begin the track
formatWriter.writeHeader();
@@ -317,8 +290,8 @@ public class TrackWriterTest extends AndroidTestCase {
formatWriter.writeEndTrack(locEq(locs[5]));
// Expect reading/writing of the waypoints (except the first)
- formatWriter.writeWaypoint(wps[1]);
- formatWriter.writeWaypoint(wps[2]);
+ formatWriter.writeWaypoint(wptEq(wps[1]));
+ formatWriter.writeWaypoint(wptEq(wps[2]));
formatWriter.writeFooter();
formatWriter.close();
@@ -330,6 +303,26 @@ public class TrackWriterTest extends AndroidTestCase {
mocksControl.verify();
}
+ private static Waypoint wptEq(final Waypoint wpt) {
+ EasyMock.reportMatcher(new IArgumentMatcher() {
+ @Override
+ public boolean matches(Object wptObj2) {
+ if (wptObj2 == null || wpt == null) return wpt == wptObj2;
+ Waypoint wpt2 = (Waypoint) wptObj2;
+
+ return wpt.getId() == wpt2.getId();
+ }
+
+ @Override
+ public void appendTo(StringBuffer buffer) {
+ buffer.append("wptEq(");
+ buffer.append(wpt);
+ buffer.append(")");
+ }
+ });
+ return null;
+ }
+
private static Location locEq(final Location loc) {
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
@@ -359,23 +352,14 @@ public class TrackWriterTest extends AndroidTestCase {
});
return null;
}
-
- private IAnswer stubCursorToArray(
- final MatrixCursor cursor, final T[] values) {
- return new IAnswer() {
- @Override
- public T answer() throws Throwable {
- return values[cursor.getPosition()];
- }
- };
- }
-
+
private void fillLocations(Location... locs) {
assertTrue(locs.length < 90);
for (int i = 0; i < locs.length; i++) {
Location location = locs[i];
location.setLatitude(i + 1);
location.setLongitude(i + 1);
+ location.setTime(i + 1000);
}
}
}
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));
}
}