Compare commits

..

6 Commits

Author SHA1 Message Date
Rodrigo Damazio c7da9a45ee Releasing v1.1.9. 2011-08-23 22:37:48 -03:00
Rodrigo Damazio aa733fbfb1 Merging to head. 2011-08-19 13:19:47 -03:00
Rodrigo Damazio d2b8f65612 Added tag v1.1.8 for changeset d4623f208a4a 2011-08-19 13:19:22 -03:00
Rodrigo Damazio cf6ac06f73 Fixing the display of waypoints. 2011-08-19 01:40:15 -03:00
Rodrigo Damazio 496ad9c4e2 Fixing menu items.
The complicated bit that was missing was the delete action, which is now its own activity with proper intent handling.
2011-08-19 01:33:46 -03:00
Rodrigo Damazio 657ec17559 Fixing usage of Charset in java.lang.String, which is only supported in API level 9. 2011-08-18 15:33:39 -03:00
103 changed files with 664 additions and 3034 deletions
-2
View File
@@ -7,5 +7,3 @@ syntax:glob
MyTracksTest/annotations.log
**/*.pyc
**/*.pyo
syntax: glob
MyTracksTest/hs_err_pid*.log
+1 -9
View File
@@ -3,16 +3,8 @@
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="lib" path="libs/googleloginclient-helper.jar"/>
<classpathentry kind="lib" path="libs/google-common.jar"/>
<classpathentry kind="lib" path="libs/google-api-client-1.2.2-alpha.jar"/>
<classpathentry kind="lib" path="libs/libGoogleAnalytics.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/commons-codec-1.3.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/commons-logging-1.1.1.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/google-api-client-1.5.0-beta.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/google-http-client-1.5.0-beta.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/google-oauth-client-1.5.0-beta.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/gson-1.6.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/guava-r09.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/jackson-core-asl-1.6.7.jar"/>
<classpathentry kind="lib" path="libs/google-api-client/jsr305-1.3.9.jar"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
+41 -41
View File
@@ -1,41 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MyTracks</name>
<comment></comment>
<projects>
<project>MyTracksLib</project>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>MyTracksLib_src</name>
<type>2</type>
<locationURI>_android_MyTracksLib_e2802071/src</locationURI>
</link>
</linkedResources>
</projectDescription>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>MyTracks</name>
<comment></comment>
<projects>
<project>MyTracksLib</project>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>MyTracksLib_src</name>
<type>2</type>
<locationURI>_android_MyTracksLib_e2802071/src</locationURI>
</link>
</linkedResources>
</projectDescription>
+5 -15
View File
@@ -17,11 +17,10 @@ limitations under the License.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.maps.mytracks"
android:installLocation="auto"
android:versionCode="33"
android:versionName="1.1.10">
android:versionCode="32"
android:versionName="1.1.9">
<application android:icon="@drawable/arrow_icon"
android:label="@string/app_name"
android:name="com.google.android.apps.mytracks.MyTracksApplication"
android:backupAgent="com.google.android.apps.mytracks.io.backup.MyTracksBackupAgent">
<uses-library android:name="com.google.android.maps" />
@@ -178,9 +177,6 @@ limitations under the License.
android:exported="true"
android:permission="com.google.android.apps.mytracks.WRITE_TRACK_DATA" />
<service android:name="com.google.android.apps.mytracks.services.ControlRecordingService"
android:exported="false" />
<receiver android:name="com.google.android.apps.mytracks.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
@@ -192,6 +188,7 @@ limitations under the License.
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.google.android.apps.mytracks.TRACK_STARTED" />
<action android:name="com.google.android.apps.mytracks.TRACK_STOPPED" />
<action android:name="com.google.android.apps.mytracks.TRACK_UPDATED" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/track_widget_info" />
@@ -233,7 +230,7 @@ limitations under the License.
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/notification_permission_label"
android:description="@string/notification_permission_description"
android:protectionLevel="dangerous" />
android:protectionLevel="normal" />
<!-- Permission to read raw track data (points, etc.) -->
<permission android:name="com.google.android.apps.mytracks.READ_TRACK_DATA"
android:permissionGroup="android.permission-group.LOCATION"
@@ -247,17 +244,10 @@ limitations under the License.
android:description="@string/write_tracks_permission_description"
android:protectionLevel="dangerous" />
<!-- Permission to get MyTracks broadcasts. For internal MyTracks communication. -->
<permission android:name="com.google.android.apps.mytracks.MYTRACKS_NOTIFICATIONS"
android:permissionGroup="android.permission-group.LOCATION"
android:label="@string/mytracks_notifications_permission_label"
android:description="@string/mytracks_notifications_permission_description"
android:protectionLevel="signature" />
<!-- We read and write raw track data, obviously. -->
<uses-permission android:name="com.google.android.apps.mytracks.READ_TRACK_DATA" />
<uses-permission android:name="com.google.android.apps.mytracks.WRITE_TRACK_DATA" />
<uses-permission android:name="com.google.android.apps.mytracks.MYTRACKS_NOTIFICATIONS" />
<uses-permission android:name="com.google.android.apps.mytracks.TRACK_NOTIFICATIONS" />
<!-- Features required -->
<uses-feature android:name="android.hardware.location" android:required="true"/>
+4 -2
View File
@@ -13,5 +13,7 @@ split.density=false
target=Google Inc.:Google APIs:13
android.library.reference.1=../MyTracksLib/
# Enable proguard.
proguard.config=proguard.cfg
# ProGuard is turned off while we investigate the removal of code that
# isn't actually dead from Fusion Tables. See issue 303 for an example of
# the result of that removal.
#proguard.config=proguard.cfg
+7 -15
View File
@@ -3,20 +3,12 @@ own licenses, which allow their usage for the My Tracks application. Any other
applications that wish to use these files should consult the appropriate
licenses or the JAR's authors.
google-api-client/
- see http://code.google.com/p/google-api-java-client/.
- contains Google API client library, google-api-client-1.5.0-beta.jar, and its
dependencies.
- google-api-client-1.5.0-beta.jar depends on protobuf-java-2.2.0.jar, however,
MyTracksLib already includes protobuf-java.2.3.0-lite.jar. Thus using the
newer version, protobuf-java.2.3.0-lite.jar.
- google-api-client-1.5.0-beta.jar also depends on xpp3-1.1.4c.jar, however,
some of its classes are already included in the android framework, thus not
including xpp3-1.1.4c.jar.
References are provided to the libraries:
google-common.jar
google-api-client-*.jar (GData client):
http://code.google.com/p/google-api-java-client/
googleloginclient-helper.jar
libGoogleAnalytics.jar
- see http://code.google.com/mobile/analytics/docs/.
google-common.jar,
googleloginclient-helper.jar,
libmymaps.jar (Google My Maps upload library):
Licensed exclusively for use in My Tracks and derived Android applications.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-16
View File
@@ -14,10 +14,6 @@
-dontwarn com.google.android.gsf.**
-dontwarn org.codehaus.**
-dontwarn com.google.appengine.**
-dontwarn org.apache.avalon.framework.logger.**
-dontwarn org.apache.log.**
-dontwarn org.apache.log4j.**
-dontwarn javax.servlet.**
# Make our stack traces useful
# Line numbers will be correct, file names will be replaced by "MT" since the
@@ -55,15 +51,3 @@
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# Needed by google-api-client to keep generic types and @Key annotations accessed via reflection
-keepclassmembers class * {
@com.google.api.client.util.Key <fields>;
}
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
# Needed by Guava
-dontwarn sun.misc.Unsafe
+3 -9
View File
@@ -40,9 +40,7 @@ limitations under the License.
android:singleLine="true"
android:autoText="true"
android:capitalize="words"
android:selectAllOnFocus="true"
android:hint="@string/trackdetails_name_hint"
android:imeOptions="actionNext"/>
android:selectAllOnFocus="true"/>
</LinearLayout>
<TextView android:text="@string/trackdetails_category"
android:layout_width="fill_parent"
@@ -50,9 +48,7 @@ limitations under the License.
<AutoCompleteTextView android:id="@+id/trackdetails_category"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:hint="@string/trackdetails_category_hint"
android:imeOptions="actionNext"/>
android:singleLine="true"/>
<TextView android:text="@string/trackdetails_description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
@@ -65,9 +61,7 @@ limitations under the License.
android:selectAllOnFocus="true"
android:maxLines="50"
android:gravity="top"
android:layout_weight="1"
android:hint="@string/trackdetails_description_hint"
android:imeOptions="actionDone"/>
android:layout_weight="1"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
+15 -13
View File
@@ -127,24 +127,26 @@ limitations under the License.
</TableLayout>
</ScrollView>
<LinearLayout
android:background="@drawable/list_bar"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="horizontal"
android:paddingLeft="2dip"
android:paddingRight="2dip"
android:paddingTop="4dip">
<!-- left spacer -->
<LinearLayout style="@style/DialogButtonSpacer"/>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_bar"
android:padding="3dip">
<Button android:id="@+id/sendtogoogle_cancel"
android:layout_gravity="left"
android:layout_width="wrap_content"
android:layout_height="48dip"
android:layout_gravity="right"
android:text="@string/cancel"
style="@style/DialogButton"/>
android:textSize="14dip"/>
<TextView
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button android:id="@+id/sendtogoogle_send_now"
android:layout_width="wrap_content"
android:layout_height="48dip"
android:layout_gravity="right"
android:text="@string/sendtogoogle_send_now"
style="@style/DialogButton"/>
<!-- right spacer -->
<LinearLayout style="@style/DialogButtonSpacer"/>
android:textSize="14dip"/>
</LinearLayout>
</LinearLayout>
@@ -50,9 +50,7 @@ limitations under the License.
android:singleLine="true"
android:autoText="true"
android:capitalize="words"
android:selectAllOnFocus="true"
android:hint="@string/waypointdetails_name_hint"
android:imeOptions="actionNext"/>
android:selectAllOnFocus="true"/>
</LinearLayout>
<LinearLayout android:id="@+id/waypointdetails_description_layout"
android:orientation="vertical"
@@ -67,9 +65,7 @@ limitations under the License.
<AutoCompleteTextView android:id="@+id/waypointdetails_category"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:hint="@string/waypointdetails_category_hint"
android:imeOptions="actionNext"/>
android:singleLine="true"/>
<TextView android:text="@string/waypointdetails_description"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
@@ -83,9 +79,7 @@ limitations under the License.
android:minLines="5"
android:maxLines="50"
android:gravity="top"
android:layout_weight="1"
android:hint="@string/waypointdetails_description_hint"
android:imeOptions="actionDone"/>
android:layout_weight="1"/>
</LinearLayout>
<TableLayout android:id="@+id/waypoint_stats"
-21
View File
@@ -125,27 +125,6 @@ limitations under the License.
android:text="@string/bpm" />
</TableRow>
<!-- Battery Level -->
<TableRow
android:padding="1dip">
<TextView
android:layout_column="1"
android:layout_span="3"
android:text="@string/battery_level" />
</TableRow>
<TableRow
android:background="@color/stat_label_background">
<TextView
style="@style/StatsRegister.Big"
android:id="@+id/battery_level_register"
android:layout_column="1"
android:layout_span="4" />
<TextView
style="@style/StatsUnitSolo"
android:layout_column="5"
android:text="@string/percent" />
</TableRow>
</TableLayout>
</LinearLayout>
</ScrollView>
-6
View File
@@ -152,20 +152,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Detalls de la ruta</string>
<string name="trackdetails_name">Nom de la ruta</string>
<string name="trackdetails_name_hint">Nom de la ruta</string>
<string name="trackdetails_description">Descripció de la ruta</string>
<string name="trackdetails_description_hint">Descripció de la ruta</string>
<string name="trackdetails_category">Tipus d\'activitat (p.e. senderisme, bicicleta, còrrer, escalada...)</string>
<string name="trackdetails_category_hint">Tipus d\'activitat</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Detalls del marcador</string>
<string name="waypointdetails_name">Nom del marcador</string>
<string name="waypointdetails_name_hint">Nom del marcador</string>
<string name="waypointdetails_description">Descripció del marcador</string>
<string name="waypointdetails_description_hint">Descripció del marcador</string>
<string name="waypointdetails_category">Tipus de marcador (e.g. edifici, sot, vista...)</string>
<string name="waypointdetails_category_hint">Tipus de marcador</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Paràmetres de My Tracks</string>
-7
View File
@@ -154,20 +154,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Trackdetails</string>
<string name="trackdetails_name">Name</string>
<string name="trackdetails_name_hint">Name</string>
<string name="trackdetails_description">Beschreibung</string>
<string name="trackdetails_description_hint">Beschreibung</string>
<string name="trackdetails_category">Aktivität (z. B. Wandern, Fahrradfahren, Laufen, Klettern…)</string>
<string name="trackdetails_category_hint">Aktivität</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Wegpunktdetails</string>
<string name="waypointdetails_name">Wegpunktname</string>
<string name="waypointdetails_name_hint">Wegpunktname</string>
<string name="waypointdetails_description">Wegpunktbeschreibung</string>
<string name="waypointdetails_description_hint">Wegpunktbeschreibung</string>
<string name="waypointdetails_category">Art des Wegpunktes (z. B. Gebäude, Schlagloch, Sehenswürdigkeit…)</string>
<string name="waypointdetails_category_hint">Art des Wegpunktes</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Einstellungen für My Tracks</string>
@@ -339,7 +333,6 @@ limitations under the License.
<!--string name="revolutions_per_minute">rpm</string-->
<!--string name="bpm">bpm</string-->
<string name="battery_level">Akkukapazität</string>
<!-- Stats Labels -->
<string name="moving_time_label">Zeit in Bewegung</string>
-6
View File
@@ -138,20 +138,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Detalles del recorrido</string>
<string name="trackdetails_name">Nombre del recorrido</string>
<string name="trackdetails_name_hint">Nombre del recorrido</string>
<string name="trackdetails_description">Descripción del recorrido</string>
<string name="trackdetails_description_hint">Descripción del recorrido</string>
<string name="trackdetails_category">Tipo de actividad (por ejemplo, ciclismo, alpinismo...)</string>
<string name="trackdetails_category_hint">Tipo de actividad</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Detalles de señal</string>
<string name="waypointdetails_name">Nombre de señal</string>
<string name="waypointdetails_name_hint">Nombre de señal</string>
<string name="waypointdetails_description">Descripción de señal</string>
<string name="waypointdetails_description_hint">Descripción de señal</string>
<string name="waypointdetails_category">Tipo de señal (por ejemplo, edificio, bache, atracción...)</string>
<string name="waypointdetails_category_hint">Tipo de señal</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Configuraciones de My Tracks</string>
+1 -7
View File
@@ -183,20 +183,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Reitin tiedot</string>
<string name="trackdetails_name">Reitin nimi</string>
<string name="trackdetails_name_hint">Reitin nimi</string>
<string name="trackdetails_description">Reitin kuvaus</string>
<string name="trackdetails_description_hint">Reitin kuvaus</string>
<string name="trackdetails_category">Liikunnan muoto (esim. hölkkä, pyöräily, juoksu...)</string>
<string name="trackdetails_category_hint">Liikunnan muoto</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Merkinnän tiedot</string>
<string name="waypointdetails_name">Merkinnän nimi</string>
<string name="waypointdetails_name_hint">Merkinnän nimi</string>
<string name="waypointdetails_description">Merkinnän kuvaus</string>
<string name="waypointdetails_description_hint">Merkinnän kuvaus</string>
<string name="waypointdetails_category">Merkinnän tyyppi (esim. rakennus, kuoppa, nähtävyys...)</string>
<string name="waypointdetails_category_hint">Merkinnän tyyppi</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks Asetukset</string>
@@ -386,9 +380,9 @@ limitations under the License.
<!-- Sensors -->
<string name="watts">W</string>
<string name="revolutions_per_minute">rpm</string>
<string name="bpm">bpm</string>
<string name="battery_level">Akku</string>
<!-- Stats Labels -->
<string name="moving_time_label">Liikkumisaika</string>
-6
View File
@@ -144,20 +144,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Détails parcours</string>
<string name="trackdetails_name">Nom du parcours</string>
<string name="trackdetails_name_hint">Nom du parcours</string>
<string name="trackdetails_description">Description du parcours</string>
<string name="trackdetails_description_hint">Description du parcours</string>
<string name="trackdetails_category">Type d\'activité (randonnée, cyclisme, jogging, escalade...)</string>
<string name="trackdetails_category_hint">Type d\'activité</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Details balise</string>
<string name="waypointdetails_name">Nom de la balise</string>
<string name="waypointdetails_name_hint">Nom de la balise</string>
<string name="waypointdetails_description">Description balise</string>
<string name="waypointdetails_description_hint">Description balise</string>
<string name="waypointdetails_category">Nom de balise (par ex.: immeuble, boutique, point de vue...)</string>
<string name="waypointdetails_category_hint">Nom de balise</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Paramètres My Tracks</string>
-6
View File
@@ -147,20 +147,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Dettagli del percorso</string>
<string name="trackdetails_name">Nome del percorso</string>
<string name="trackdetails_name_hint">Nome del percorso</string>
<string name="trackdetails_description">Descrizione del percorso</string>
<string name="trackdetails_description_hint">Descrizione del percorso</string>
<string name="trackdetails_category">Tipo di attività (ad es. escursionismo, ciclismo, corsa, arrampicata...)</string>
<string name="trackdetails_category_hint">Tipo di attività</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Dettagli sul segnaposto</string>
<string name="waypointdetails_name">Nome del segnaposto</string>
<string name="waypointdetails_name_hint">Nome del segnaposto</string>
<string name="waypointdetails_description">Descrizione del segnaposto</string>
<string name="waypointdetails_description_hint">Descrizione del segnaposto</string>
<string name="waypointdetails_category">Tipo di segnaposto (ad es. edificio, buca, vista...)</string>
<string name="waypointdetails_category_hint">Tipo di segnaposto</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Impostazioni di My Tracks</string>
-6
View File
@@ -176,20 +176,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">トラック詳細</string>
<string name="trackdetails_name">トラック名</string>
<string name="trackdetails_name_hint">トラック名</string>
<string name="trackdetails_description">トラック説明</string>
<string name="trackdetails_description_hint">トラック説明</string>
<string name="trackdetails_category">活動タイプ(ハイキング, サイクリング、ランニング、登山...)</string>
<string name="trackdetails_category_hint">活動タイプ</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">マーカー詳細</string>
<string name="waypointdetails_name">マーカー名</string>
<string name="waypointdetails_name_hint">マーカー名</string>
<string name="waypointdetails_description">マーカー説明</string>
<string name="waypointdetails_description_hint">マーカー説明</string>
<string name="waypointdetails_category">マーカータイプ(建物、洞穴、名所...)</string>
<string name="waypointdetails_category_hint">マーカータイプ</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks設定</string>
-6
View File
@@ -149,20 +149,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Szczegóły trasy</string>
<string name="trackdetails_name">Nazwa trasy</string>
<string name="trackdetails_name_hint">Nazwa trasy</string>
<string name="trackdetails_description">Opis trasy</string>
<string name="trackdetails_description_hint">Opis trasy</string>
<string name="trackdetails_category">Typ aktywności (np. wspinanie, jazda rowerem, bieganie...)</string>
<string name="trackdetails_category_hint">Typ aktywności</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Szczegóły znacznika</string>
<string name="waypointdetails_name">Nazwa znacznika</string>
<string name="waypointdetails_name_hint">Nazwa znacznika</string>
<string name="waypointdetails_description">Opis znacznika</string>
<string name="waypointdetails_description_hint">Opis znacznika</string>
<string name="waypointdetails_category">Typ znacznika (np. budynek, jaskinia, punkt widokowy...)</string>
<string name="waypointdetails_category_hint">Typ znacznika</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Ustawienia My Tracks</string>
-6
View File
@@ -144,20 +144,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Detalhes do Caminho</string>
<string name="trackdetails_name">Nome do Caminho</string>
<string name="trackdetails_name_hint">Nome do Caminho</string>
<string name="trackdetails_description">Descrição do Caminho</string>
<string name="trackdetails_description_hint">Descrição do Caminho</string>
<string name="trackdetails_category">Tipo de Atividade (por ex. caminhada, ciclismo, escalada...)</string>
<string name="trackdetails_category_hint">Tipo de Atividade</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Detalhes do Marcador</string>
<string name="waypointdetails_name">Nome do Marcador</string>
<string name="waypointdetails_name_hint">Nome do Marcador</string>
<string name="waypointdetails_description">Descrição do Marcador</string>
<string name="waypointdetails_description_hint">Descrição do Marcador</string>
<string name="waypointdetails_category">Tipo do Marcador (por ex. Prédio, Mirante, Cachoeira...)</string>
<string name="waypointdetails_category_hint">Tipo do Marcador</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Configurações do My Tracks</string>
-6
View File
@@ -179,20 +179,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Трек детали</string>
<string name="trackdetails_name">Название трека</string>
<string name="trackdetails_name_hint">Название трека</string>
<string name="trackdetails_description">Описание трека</string>
<string name="trackdetails_description_hint">Описание трека</string>
<string name="trackdetails_category">Тип деятельности (например: пешая прогулка, велосипедная прогулка, бег, восхождение...)</string>
<string name="trackdetails_category_hint">Тип деятельности</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Маркер детали</string>
<string name="waypointdetails_name">Имя маркера</string>
<string name="waypointdetails_name_hint">Имя маркера</string>
<string name="waypointdetails_description">Описание маркера</string>
<string name="waypointdetails_description_hint">Описание маркера</string>
<string name="waypointdetails_category">Тип маркера (например: здание, выбоина, достопримечательность...)</string>
<string name="waypointdetails_category_hint">Тип маркера</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">Настройки My Tracks</string>
-6
View File
@@ -149,20 +149,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Spårdetaljer</string>
<string name="trackdetails_name">Spårnamn</string>
<string name="trackdetails_name_hint">Spårnamn</string>
<string name="trackdetails_description">Spårbeskrivning</string>
<string name="trackdetails_description_hint">Spårbeskrivning</string>
<string name="trackdetails_category">Aktivitetstyp (t ex vandring, cyckling, löpning, klättring...)</string>
<string name="trackdetails_category_hint">Aktivitetstyp</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Markördetaljer</string>
<string name="waypointdetails_name">Markörnamn</string>
<string name="waypointdetails_name_hint">Markörnamn</string>
<string name="waypointdetails_description">Markörbeskrivning</string>
<string name="waypointdetails_description_hint">Markörbeskrivning</string>
<string name="waypointdetails_category">Markörtyp (e.g. byggnad, grop, sevärdhet...)</string>
<string name="waypointdetails_category_hint">Markörtyp</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks inställningar</string>
-6
View File
@@ -180,20 +180,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">轨迹详细信息</string>
<string name="trackdetails_name">轨迹名称</string>
<string name="trackdetails_name_hint">轨迹名称</string>
<string name="trackdetails_description">轨迹描述</string>
<string name="trackdetails_description_hint">轨迹描述</string>
<string name="trackdetails_category">活动类型 (例如: 徒步, 骑行, 跑步, 攀岩...)</string>
<string name="trackdetails_category_hint">活动类型</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">标记详细信息</string>
<string name="waypointdetails_name">标记名称</string>
<string name="waypointdetails_name_hint">标记名称</string>
<string name="waypointdetails_description">标记描述</string>
<string name="waypointdetails_description_hint">标记描述</string>
<string name="waypointdetails_category">标记类型 (例如: 建筑, 陷坑, 景点...)</string>
<string name="waypointdetails_category_hint">标记类型</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks 设置</string>
@@ -118,16 +118,4 @@ limitations under the License.
<item>@string/ant_sensor_type</item>
<item>@string/srm_ant_bridge_sensor_type</item>
</string-array>
<string-array name="track_color_mode_options">
<item>@string/track_color_mode_none</item>
<item>@string/track_color_mode_fixed</item>
<item>@string/track_color_mode_dynamic</item>
</string-array>
<string-array name="track_color_mode_values">
<item>@string/none</item>
<item>@string/track_color_mode_fixed</item>
<item>@string/track_color_mode_dynamic</item>
</string-array>
</resources>
-10
View File
@@ -51,14 +51,4 @@ limitations under the License.
<!-- AppWidget colors -->
<color name="appwidget_text">#EDEDED</color>
<color name="appwidget_hr">#99999999</color>
<!-- Colors for dynamic track drawing -->
<!-- Green -->
<color name="slow_path">#F774DF00</color>
<!-- Blue -->
<color name="normal_path">#F75858FA</color>
<!-- Red -->
<color name="fast_path">#F7DF0101</color>
</resources>
-11
View File
@@ -43,18 +43,7 @@ limitations under the License.
<!-- ID for Google Analytics -->
<string name="google_analytics_id">UA-7222692-2</string>
<!-- Symbols -->
<string name="percent">%</string>
<!-- Intent actions -->
<string name="save_intent_action">android.intent.action.SAVE</string>
<string name="open_settings_screen">android.intent.action.settings.OPEN</string>
<string name="start_new_track_action">com.google.android.apps.mytracks.START_NEW_TRACK</string>
<string name="end_current_track_action">com.google.android.apps.mytracks.END_CURRENT_TRACK
</string>
<!-- Permissions -->
<!-- Permission for MyTracks notifications. -->
<string name="mytracks_notifications_permission">com.google.android.apps.mytracks.MYTRACKS_NOTIFICATIONS</string>
</resources>
+7 -20
View File
@@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Keys for application preferences. Sorted alphabetically. - DO NOT TRANSLATE -->
<string name="allow_access_key">allowAccess</string>
<!-- Keys for application preferences - DO NOT TRANSLATE -->
<string name="announcement_frequency_key">announcementFrequency</string>
<string name="ant_heart_rate_sensor_id_key">antHeartRateSensorId</string>
<string name="ant_power_sensor_id_key">antPowerSensorId</string>
@@ -36,34 +35,22 @@ limitations under the License.
<string name="report_speed_key">reportSpeed</string>
<string name="send_stats_and_points_key">sendStatsAndPoints</string>
<string name="send_to_docs_key">sendToDocs</string>
<string name="send_to_fusion_tables_key">sendToFusionTables</string>
<string name="send_to_my_maps_key">sendToMyMaps</string>
<string name="send_to_fusion_tables_key">sendToFusionTables</string>
<string name="sensor_type_key">sensorType</string>
<string name="share_url_only_key">shareUrlOnly</string>
<string name="split_frequency_key">splitFrequency</string>
<string name="timestamp_track_name_key">timestampTrackName</string>
<string name="track_color_mode_dynamic_speed_variation_key">trackColorModeDynamicVariation
</string>
<string name="track_color_mode_fixed_speed_medium_display_key">trackColorModeFixedMediumDisplay
</string>
<string name="track_color_mode_fixed_speed_medium_key">trackColorModeFixedMedium</string>
<string name="track_color_mode_fixed_speed_slow_display_key">trackColorModeFixedSlowDisplay
</string>
<string name="track_color_mode_fixed_speed_slow_key">trackColorModeFixedSlow</string>
<string name="track_color_mode_key">trackColorMode</string>
<!-- Preference keys which keep transient state (not user preferences). Sorted alphabetically.
- DO NOT TRANSLATE -->
<!-- Preference keys which keep transient state (not user preferences) - DO NOT TRANSLATE -->
<string name="auto_resume_track_current_retry_key">autoResumeTrackCurrentRetry</string>
<string name="recording_track_key">recordingTrack</string>
<string name="selected_track_key">selectedTrack</string>
<!-- Keys for non-persisted preferences. Sorted alphabetically. - DO NOT TRANSLATE -->
<string name="ant_options_key">antOptions</string>
<!-- Keys for non-persisted preferences - DO NOT TRANSLATE -->
<string name="backup_to_sd_key">backupToSd</string>
<string name="display_settings_screen_key">displaySettingsScreen</string>
<string name="reset_key">resetToDefaults</string>
<string name="restore_from_sd_key">restoreFromSd</string>
<string name="reset_key">resetToDefaults</string>
<string name="sensor_options_key">sensorOptions</string>
<string name="track_color_mode_fixed_speed_options_key">trackColorModeFixedOptions</string>
<string name="ant_options_key">antOptions</string>
</resources>
+7 -48
View File
@@ -184,20 +184,14 @@ limitations under the License.
<!-- MyTracks Details -->
<string name="trackdetails_title">Track Details</string>
<string name="trackdetails_name">Track Name</string>
<string name="trackdetails_name_hint">Track Name</string>
<string name="trackdetails_description">Track Description</string>
<string name="trackdetails_description_hint">Track Description</string>
<string name="trackdetails_category">Activity Type (e.g. hiking, cycling, running, climbing...)</string>
<string name="trackdetails_category_hint">Activity Type</string>
<!-- MyTracks Waypoint Details -->
<string name="waypointdetails_title">Marker Details</string>
<string name="waypointdetails_name">Marker Name</string>
<string name="waypointdetails_name_hint">Marker Name</string>
<string name="waypointdetails_description">Marker Description</string>
<string name="waypointdetails_description_hint">Marker Description</string>
<string name="waypointdetails_category">Marker Type (e.g. Building, Pothole, Sight...)</string>
<string name="waypointdetails_category_hint">Marker Type</string>
<!-- MyTracks Settings -->
<string name="mytracks_settings">My Tracks Settings</string>
@@ -208,15 +202,6 @@ limitations under the License.
<string name="settings_not_available_summary">Not available in your version of Android</string>
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_allow_access">Allow access</string>
<string name="settings_allow_access_summary_on">Other apps can read, write, start/stop recording
MyTracks data.</string>
<string name="settings_allow_access_summary_off">Other apps cannot read, write, start/stop
recording MyTracks data.</string>
<string name="settings_allow_access_dialog_message">Are you sure you want to allow other apps,
including future apps, to control and access MyTracks data? There will be no further
notification.</string>
<string name="settings_announcement_frequency">Announcement frequency</string>
<string name="settings_announcement_frequency_summary">Minimum time between two voice announcements</string>
@@ -277,17 +262,6 @@ limitations under the License.
<string name="bluetooth_sensor_summary">The bluetooth sensor to connect to</string>
<string name="settings_modular_stats_display_title">Modular Stats Display</string>
<string name="settings_track_color_mode">Track Color Mode</string>
<string name="settings_track_color_mode_summary">The way to color the track</string>
<string name="settings_track_color_mode_fixed">Fixed</string>
<string name="settings_track_color_mode_dynamic">Dynamic</string>
<string name="settings_track_color_mode_fixed_speed_slow">Fixed Speed Slow</string>
<string name="settings_track_color_mode_fixed_speed_slow_summary">Slow speed top value</string>
<string name="settings_track_color_mode_fixed_speed_medium">Fixed Speed Medium</string>
<string name="settings_track_color_mode_fixed_speed_medium_summary">Medium speed top value</string>
<string name="settings_track_color_mode_dynamic_speed_variation">Dynamic Speed Variation</string>
<string name="settings_track_color_mode_dynamic_speed_variation_summary">% Margin from average speed to consider medium speed</string>
<!-- Backups -->
<string name="settings_backup">Backup (BETA)</string>
<string name="settings_sd_backup">SD Card Backup</string>
@@ -410,7 +384,6 @@ limitations under the License.
<string name="revolutions_per_minute">rpm</string>
<string name="bpm">bpm</string>
<string name="battery_level">Battery Level</string>
<!-- Stats Labels -->
<string name="moving_time_label">Moving Time</string>
@@ -503,10 +476,7 @@ limitations under the License.
<string name="sensor_bluetooth_options_key">bluetoothSensorsEnabled</string>
<string name="ant_options">ANT options</string>
<string name="sensor_options">Sensor Options</string>
<string name="track_color_mode">Track Color Mode</string>
<string name="fixed_speed_options">Fixed Speed Options</string>
<string name="dynamic_speed_options">Dynamic Speed Options</string>
<!-- Bluetooth Sensor -->
<string name="bluetooth_connected">Connected to %s</string>
<string name="bluetooth_connecting">Bluetooth sensor connecting ...</string>
@@ -526,19 +496,12 @@ limitations under the License.
<string name="sending">Sending</string>
<!-- Manifest description (not seen inside the app) -->
<string name="notification_permission_label">receive MyTracks notifications</string>
<string name="mytracks_notifications_permission_label">receive MyTracks notifications</string>
<string name="read_tracks_permission_label">read MyTracks data</string>
<string name="write_tracks_permission_label">write data to MyTracks and start/stop MyTracks
recording</string>
<string name="notification_permission_description">Allows the application to receive notifications
when MyTracks starts or stops a recording.</string>
<string name="mytracks_notifications_permission_description">receive MyTracks notifications
</string>
<string name="read_tracks_permission_description">Allows the application to read MyTracks data.
</string>
<string name="write_tracks_permission_description">Allows the application to write data to
MyTracks and start/stop MyTracks recording.</string>
<string name="notification_permission_label">receive track recording notifications</string>
<string name="read_tracks_permission_label">read recorded track data</string>
<string name="write_tracks_permission_label">write recorded track data, start/stop recording tracks</string>
<string name="notification_permission_description">Be notified when a track starts or stops being recorded.</string>
<string name="read_tracks_permission_description">Be able to read track data, such as points and waypoints.</string>
<string name="write_tracks_permission_description">Be able to write track data, such as starting recording or adding waypoints.</string>
<!-- Sensor Type Options -->
<string name="no_sensor_option">NONE</string>
@@ -547,8 +510,4 @@ limitations under the License.
<string name="polar_bt_sensor_option">Polar Bluetooth</string>
<string name="zephyr_bt_sensor_option">Zephyr Bluetooth</string>
<!-- Track Color Modes -->
<string name="track_color_mode_none">None</string>
<string name="track_color_mode_fixed">Fixed (BETA)</string>
<string name="track_color_mode_dynamic">Dynamic (BETA)</string>
</resources>
-18
View File
@@ -85,22 +85,4 @@ limitations under the License.
<style name="WidgetText.Register">
<item name="android:width">160px</item>
</style>
<!-- The style for the buttons on the bottom of a dialog. -->
<style name="DialogButton">
<item name="android:layout_height">48dip</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_width">0dip</item>
<item name="android:maxLines">2</item>
<item name="android:textSize">14dip</item>
</style>
<!-- The style for the spacer on the left and right of the dialog buttons. -->
<style name="DialogButtonSpacer">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">0.25</item>
<item name="android:layout_width">0dip</item>
<item name="android:orientation">horizontal</item>
<item name="android:visibility">gone</item>
</style>
</resources>
+1 -61
View File
@@ -18,8 +18,7 @@ limitations under the License.
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Display settings -->
<PreferenceScreen android:title="@string/settings_display"
android:key="@string/display_settings_screen_key">
<PreferenceScreen android:title="@string/settings_display">
<CheckBoxPreference
android:key="@string/metric_units_key"
android:title="@string/settings_metric_units"
@@ -33,58 +32,6 @@ limitations under the License.
android:summaryOn="@string/settings_report_speed_or_pace_summary_on"
android:summaryOff="@string/settings_report_speed_or_pace_summary_off"
android:defaultValue="true" />
<ListPreference android:key="@string/track_color_mode_key"
android:title="@string/settings_track_color_mode"
android:summary="@string/settings_track_color_mode_summary"
android:entries="@array/track_color_mode_options"
android:entryValues="@array/track_color_mode_values"
android:defaultValue="@string/none"/>
<PreferenceCategory android:title="@string/fixed_speed_options"
android:key="@string/track_color_mode_fixed_speed_options_key">
<EditTextPreference android:key="@string/track_color_mode_fixed_speed_slow_key"
android:title="@string/settings_track_color_mode_fixed_speed_slow"
android:summary="@string/settings_track_color_mode_fixed_speed_slow_summary"
android:defaultValue="9"
android:inputType="number"
android:maxLength="3">
</EditTextPreference>
<EditTextPreference android:key="@string/track_color_mode_fixed_speed_slow_display_key"
android:title="@string/settings_track_color_mode_fixed_speed_slow"
android:summary="@string/settings_track_color_mode_fixed_speed_slow_summary"
android:defaultValue="9"
android:inputType="number"
android:maxLength="3">
</EditTextPreference>
<EditTextPreference android:key="@string/track_color_mode_fixed_speed_medium_key"
android:title="@string/settings_track_color_mode_fixed_speed_medium"
android:summary="@string/settings_track_color_mode_fixed_speed_medium_summary"
android:defaultValue="15"
android:inputType="number"
android:maxLength="3">
</EditTextPreference>
<EditTextPreference android:key="@string/track_color_mode_fixed_speed_medium_display_key"
android:title="@string/settings_track_color_mode_fixed_speed_medium"
android:summary="@string/settings_track_color_mode_fixed_speed_medium_summary"
android:defaultValue="15"
android:inputType="number"
android:maxLength="3">
</EditTextPreference>
</PreferenceCategory>
<PreferenceCategory android:title="@string/dynamic_speed_options">
<EditTextPreference android:key="@string/track_color_mode_dynamic_speed_variation_key"
android:title="@string/settings_track_color_mode_dynamic_speed_variation"
android:summary="@string/settings_track_color_mode_dynamic_speed_variation_summary"
android:defaultValue="25"
android:inputType="number"
android:maxLength="2">
</EditTextPreference>
</PreferenceCategory>
</PreferenceScreen>
<!-- Recording settings -->
@@ -209,13 +156,6 @@ limitations under the License.
android:summaryOn="@string/settings_share_url_only_summary_on"
android:summaryOff="@string/settings_share_url_only_summary_off"
android:defaultValue="false" />
<CheckBoxPreference
android:key="@string/allow_access_key"
android:title="@string/settings_allow_access"
android:summaryOn="@string/settings_allow_access_summary_on"
android:summaryOff="@string/settings_allow_access_summary_off"
android:defaultValue="false" />
</PreferenceScreen>
<!-- Backup settings -->
@@ -20,11 +20,11 @@ import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.ChartView.Mode;
import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
@@ -148,7 +148,7 @@ public class ChartActivity extends Activity implements TrackDataListener {
protected void onResume() {
super.onResume();
dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
dataHub = TrackDataHub.getStartedInstance();
dataHub.registerTrackDataListener(this, EnumSet.of(
ListenerDataType.SELECTED_TRACK_CHANGED,
ListenerDataType.TRACK_UPDATES,
@@ -25,7 +25,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import java.util.Locale;
/**
@@ -80,8 +79,6 @@ class CheckUnits {
private static void accept(Context context, SharedPreferences preferences) {
recordCheckPerformed(preferences);
Intent startIntent = new Intent(context, SettingsActivity.class);
startIntent.putExtra(context.getString(R.string.open_settings_screen),
context.getString(R.string.display_settings_screen_key));
context.startActivity(startIntent);
}
@@ -1,62 +0,0 @@
/*
* Copyright 2011 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 android.graphics.Paint;
import android.graphics.Path;
/**
* Represents a colored {@code Path} to save its relative color for drawing.
* @author Vangelis S.
*/
public class ColoredPath {
private final Path path;
private final Paint pathPaint;
/**
* Constructor for a ColoredPath by color.
*/
public ColoredPath(int color) {
path = new Path();
pathPaint = new Paint();
pathPaint.setColor(color);
pathPaint.setStrokeWidth(3);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setAntiAlias(true);
}
/**
* Constructor for a ColoredPath by Paint.
*/
public ColoredPath(Paint paint) {
path = new Path();
pathPaint = paint;
}
/**
* @return the path
*/
public Path getPath() {
return path;
}
/**
* @return the pathPaint
*/
public Paint getPathPaint() {
return pathPaint;
}
}
@@ -200,7 +200,7 @@ public abstract class Constants {
}
public static final String MAPSHOP_BASE_URL =
"https://maps.google.com/maps/ms";
"http://maps.google.com/maps/ms";
/*
* Default values - keep in sync with those in preferences.xml.
@@ -95,15 +95,6 @@ public class ImportAllTracks {
Log.i(Constants.TAG, "ImportAllTracks: Releasing wake lock.");
}
activity.runOnUiThread(new Thread() {
@Override
public void run() {
showDoneDialog();
}
});
}
private void showDoneDialog() {
Log.i(Constants.TAG, "ImportAllTracks: Done");
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
if (gpxFileCount == 0) {
@@ -217,4 +208,4 @@ public class ImportAllTracks {
}
return gpxFiles;
}
}
}
@@ -185,7 +185,7 @@ public class MapActivity extends com.google.android.maps.MapActivity
Log.d(TAG, "MapActivity.onResume");
super.onResume();
dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
dataHub = TrackDataHub.getStartedInstance();
dataHub.registerTrackDataListener(this, EnumSet.of(
ListenerDataType.SELECTED_TRACK_CHANGED,
ListenerDataType.POINT_UPDATES,
@@ -18,9 +18,6 @@ package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.maps.TrackPathPainter;
import com.google.android.apps.mytracks.maps.TrackPathPainterFactory;
import com.google.android.apps.mytracks.maps.TrackPathUtilities;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
@@ -30,11 +27,10 @@ import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -52,7 +48,7 @@ import java.util.concurrent.BlockingQueue;
*
* @author Leif Hendrik Wilden
*/
public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListener {
public class MapOverlay extends Overlay {
private final Drawable[] arrows;
private final int arrowWidth, arrowHeight;
@@ -61,6 +57,7 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
private final Drawable startMarker;
private final Drawable endMarker;
private final int markerWidth, markerHeight;
private final Paint selectedTrackPaint;
private final Paint errorCirclePaint;
private final Context context;
private final List<Waypoint> waypoints;
@@ -76,26 +73,22 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
private GeoPoint lastReferencePoint;
private Rect lastViewRect;
private boolean lastPathExists;
private Location lastLocation;
private TrackPathPainter trackPathPainter;
private Path lastPath;
/**
* Represents a pre-processed {@code Location} to speed up drawing.
* This class is more like a data object and doesn't provide accessors.
*/
public static class CachedLocation {
private static class CachedLocation {
public final boolean valid;
public final GeoPoint geoPoint;
public final int speed;
/**
* Constructor for an invalid cached location.
*/
public CachedLocation() {
this.valid = false;
this.geoPoint = null;
this.speed = -1;
}
/**
@@ -104,7 +97,6 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
public CachedLocation(Location location) {
this.valid = LocationUtils.isValidLocation(location);
this.geoPoint = valid ? LocationUtils.getGeoPoint(location) : null;
this.speed = (int) Math.floor(location.getSpeed() * 3.6);
}
};
@@ -159,13 +151,18 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
waypointMarker = resources.getDrawable(R.drawable.blue_pushpin);
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
errorCirclePaint = TrackPathUtilities.getPaint(R.color.blue, context);
selectedTrackPaint = new Paint();
selectedTrackPaint.setColor(resources.getColor(R.color.red));
selectedTrackPaint.setStrokeWidth(3);
selectedTrackPaint.setStyle(Paint.Style.STROKE);
selectedTrackPaint.setAntiAlias(true);
errorCirclePaint = new Paint();
errorCirclePaint.setColor(resources.getColor(R.color.blue));
errorCirclePaint.setStyle(Paint.Style.STROKE);
errorCirclePaint.setStrokeWidth(3);
errorCirclePaint.setAlpha(127);
trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context);
context.getSharedPreferences(Constants.SETTINGS_NAME, 0)
.registerOnSharedPreferenceChangeListener(this);
errorCirclePaint.setAntiAlias(true);
}
/**
@@ -208,19 +205,18 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
}
// Visible for testing.
public int getNumWaypoints() {
int getNumWaypoints() {
synchronized (waypoints) {
return waypoints.size();
}
}
public void clearPoints() {
synchronized (getPoints()) {
getPoints().clear();
synchronized (points) {
points.clear();
pendingPoints.clear();
lastPathExists = false;
lastPath = null;
lastViewRect = null;
trackPathPainter.clear();
}
}
@@ -258,9 +254,6 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
// Draw the selected track:
drawTrack(canvas, projection, viewRect);
// Draw the "Start" and "End" markers:
drawMarkers(canvas, projection);
// Draw the waypoints:
drawWaypoints(canvas, projection);
}
@@ -269,28 +262,6 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
drawMyLocation(canvas, projection);
}
private void drawMarkers(Canvas canvas, Projection projection) {
// Draw the "End" marker.
if (showEndMarker) {
for (int i = getPoints().size() - 1; i >= 0; --i) {
if (getPoints().get(i).valid) {
drawElement(canvas, projection, getPoints().get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Draw the "Start" marker.
for (int i = 0; i < getPoints().size(); ++i) {
if (getPoints().get(i).valid) {
drawElement(canvas, projection, getPoints().get(i).geoPoint, startMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Visible for testing.
Projection getMapProjection(MapView mapView) {
return mapView.getProjection();
@@ -304,15 +275,15 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
int cy = mapView.getMapCenter().getLatitudeE6();
return new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2);
}
// For use in testing only.
public TrackPathPainter getTrackPathPainter() {
return trackPathPainter;
// Visible for testing.
Path newPath() {
return new Path();
}
// For use in testing only.
public void setTrackPathPainter(TrackPathPainter trackPathPainter) {
this.trackPathPainter = trackPathPainter;
// Visible for testing.
Path getLastPath() {
return lastPath;
}
private void drawWaypoints(Canvas canvas, Projection projection) {
@@ -340,39 +311,96 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
canvas.drawCircle(pt.x, pt.y, radius, errorCirclePaint);
}
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect)
{
boolean draw;
private void drawTrack(Canvas canvas, Projection projection, Rect viewRect) {
Path path;
synchronized (points) {
// Merge the pending points with the list of cached locations.
final GeoPoint referencePoint = projection.fromPixels(0, 0);
int newPoints = pendingPoints.drainTo(points);
int newPoints = pendingPoints.drainTo(points);
boolean newProjection = !viewRect.equals(lastViewRect) ||
!referencePoint.equals(lastReferencePoint);
if (newPoints == 0 && lastPathExists && !newProjection) {
!referencePoint.equals(lastReferencePoint);
if (newPoints == 0 && lastPath != null && !newProjection) {
// No need to recreate path (same points and viewing area).
draw = true;
path = lastPath;
} else {
int numPoints = points.size();
if (numPoints < 2) {
// Not enough points to draw a path.
draw = false;
} else if (!trackPathPainter.needsRedraw() && lastPathExists && !newProjection) {
path = null;
} else if (lastPath != null && !newProjection) {
// Incremental update of the path, without repositioning the view.
draw = true;
trackPathPainter.updatePath(projection, viewRect, numPoints - newPoints, alwaysVisible, points);
path = lastPath;
updatePath(projection, viewRect, path, numPoints - newPoints);
} else {
// The view has changed so we have to start from scratch.
draw = true;
trackPathPainter.updatePath(projection, viewRect, 0, alwaysVisible, points);
path = newPath();
path.incReserve(numPoints);
updatePath(projection, viewRect, path, 0);
}
lastPath = path;
}
lastReferencePoint = referencePoint;
lastViewRect = viewRect;
}
if (draw) {
trackPathPainter.drawTrack(canvas);
if (path != null) {
canvas.drawPath(path, selectedTrackPaint);
}
// Draw the "End" marker.
if (showEndMarker) {
for (int i = points.size() - 1; i >= 0; --i) {
if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, endMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
// Draw the "Start" marker.
for (int i = 0; i < points.size(); ++i) {
if (points.get(i).valid) {
drawElement(canvas, projection, points.get(i).geoPoint, startMarker,
-markerWidth / 2, -markerHeight);
break;
}
}
}
private void updatePath(Projection projection, Rect viewRect, Path path,
int startLocationIdx) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment =
startLocationIdx > 0 ? !points.get(startLocationIdx - 1).valid : true;
boolean lastVisible = !newSegment;
final Point pt = new Point();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains(
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
projection.toPixels(geoPoint, pt);
if (newSegment) {
path.moveTo(pt.x, pt.y);
newSegment = false;
} else {
path.lineTo(pt.x, pt.y);
}
}
}
@@ -451,21 +479,4 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe
}
return super.onTap(p, mapView);
}
/**
* @return the points
*/
public List<CachedLocation> getPoints() {
return points;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "MapOverlay: onSharedPreferences changed " + key);
if (key != null) {
if (key.equals(context.getString(R.string.track_color_mode_key))) {
trackPathPainter = TrackPathPainterFactory.getTrackPathPainter(context);
}
}
}
}
@@ -146,7 +146,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
providerUtils = MyTracksProviderUtils.Factory.get(this);
preferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
dataHub = TrackDataHub.newInstance(this);
menuManager = new MenuManager(this);
serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
@@ -249,6 +249,7 @@ public class MyTracks extends TabActivity implements OnTouchListener {
protected void onDestroy() {
Log.d(TAG, "MyTracks.onDestroy");
serviceConnection.unbind();
super.onDestroy();
}
@@ -1,42 +0,0 @@
/*
* Copyright 2008 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.apps.mytracks.content.TrackDataHub;
import android.app.Application;
/**
* MyTracksApplication for keeping global state.
*
* @author jshih@google.com (Jimmy Shih)
*/
public class MyTracksApplication extends Application {
private TrackDataHub trackDataHub;
/**
* Gets the application's TrackDataHub.
*
* Note: use synchronized to make sure only one instance is created per application.
*/
public synchronized TrackDataHub getTrackDataHub() {
if (trackDataHub == null) {
trackDataHub = TrackDataHub.newInstance(getApplicationContext());
}
return trackDataHub;
}
}
@@ -20,8 +20,6 @@ import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.services.sensors.SensorManager;
import com.google.android.apps.mytracks.services.sensors.SensorManagerFactory;
import com.google.android.apps.mytracks.services.sensors.SensorUtils;
import com.google.android.maps.mytracks.R;
import com.google.protobuf.InvalidProtocolBufferException;
@@ -57,8 +55,7 @@ public class SensorStateActivity extends Activity {
private final Runnable stateUpdater = new Runnable() {
public void run() {
if (isVisible) // only update when UI is visible
updateState();
updateState();
}
};
@@ -76,23 +73,6 @@ public class SensorStateActivity extends Activity {
runOnUiThread(stateUpdater);
}
};
/**
* A temporary sensor manager, when none is available.
*/
SensorManager tempSensorManager = null;
/**
* A state flag set to true when the activity is active/visible,
* i.e. after resume, and before pause
*
* Used to avoid updating after the pause event, because sometimes an update
* event occurs even after the timer is cancelled. In this case,
* it could cause the {@link #tempSensorManager} to be recreated, after it
* is destroyed at the pause event.
*/
boolean isVisible = false;
public SensorStateActivity() {
utils = new StatsUtilities(this);
@@ -106,6 +86,7 @@ public class SensorStateActivity extends Activity {
setContentView(R.layout.sensor_state);
// TODO: Allow reading from sensors even if service is not running.
serviceConnection = new TrackRecordingServiceConnection(this, stateUpdater);
serviceConnection.bindIfRunning();
updateState();
@@ -114,8 +95,6 @@ public class SensorStateActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
isVisible = true;
serviceConnection.bindIfRunning();
@@ -125,12 +104,9 @@ public class SensorStateActivity extends Activity {
@Override
protected void onPause() {
isVisible = false;
timer.cancel();
timer.purge();
timer = null;
stopTempSensorManager();
super.onPause();
}
@@ -141,106 +117,35 @@ public class SensorStateActivity extends Activity {
}
protected void updateState() {
Log.d(TAG, "Updating SensorStateActivity");
ITrackRecordingService service = serviceConnection.getServiceIfBound();
// Check if service is available, and recording.
boolean isRecording = false;
if (service != null) {
try {
isRecording = service.isRecording();
} catch (RemoteException e) {
Log.e(TAG, "Unable to determine if service is recording.", e);
}
}
// If either service isn't available, or not recording.
if (!isRecording) {
updateFromTempSensorManager();
} else {
updateFromSysSensorManager();
}
}
private void updateFromTempSensorManager()
{
// Use variables to hold the sensor state and data set.
Sensor.SensorState currentState = null;
Sensor.SensorDataSet currentDataSet = null;
// If no temp sensor manager is present, create one, and start it.
if (tempSensorManager == null) {
tempSensorManager = SensorManagerFactory.getSensorManager(this);
if (tempSensorManager != null)
tempSensorManager.onStartTrack();
}
// If a temp sensor manager is available, use states from temp sensor
// manager.
if (tempSensorManager != null) {
currentState = tempSensorManager.getSensorState();
currentDataSet = tempSensorManager.getSensorDataSet();
}
// Update the sensor state, and sensor data, using the variables.
updateSensorStateAndData(currentState, currentDataSet);
}
private void updateFromSysSensorManager()
{
// Use variables to hold the sensor state and data set.
Sensor.SensorState currentState = null;
Sensor.SensorDataSet currentDataSet = null;
ITrackRecordingService service = serviceConnection.getServiceIfBound();
// If a temp sensor manager is present, shut it down,
// probably recording just started.
stopTempSensorManager();
// Get sensor details from the service.
if (service == null) {
Log.d(TAG, "Could not get track recording service.");
} else {
try {
byte[] buff = service.getSensorData();
if (buff != null) {
currentDataSet = Sensor.SensorDataSet.parseFrom(buff);
}
} catch (RemoteException e) {
Log.e(TAG, "Could not read sensor data.", e);
} catch (InvalidProtocolBufferException e) {
Log.e(TAG, "Could not read sensor data.", e);
}
try {
currentState = Sensor.SensorState.valueOf(service.getSensorState());
} catch (RemoteException e) {
Log.e(TAG, "Could not read sensor state.", e);
currentState = Sensor.SensorState.NONE;
}
Log.d(Constants.TAG, "Could not get track recording service.");
updateSensorState(Sensor.SensorState.NONE);
updateSensorData(null);
return;
}
// Update the sensor state, and sensor data, using the variables.
updateSensorStateAndData(currentState, currentDataSet);
}
/**
* Stops the temporary sensor manager, if one exists.
*/
private void stopTempSensorManager()
{
if (tempSensorManager != null) {
tempSensorManager.shutdown();
tempSensorManager = null;
Sensor.SensorDataSet sds = null;
try {
byte[] buff = service.getSensorData();
if (buff != null) {
sds = Sensor.SensorDataSet.parseFrom(buff);
updateSensorData(sds);
}
} catch (RemoteException e) {
Log.e(Constants.TAG, "Could not read sensor data.", e);
} catch (InvalidProtocolBufferException e) {
Log.e(Constants.TAG, "Could not read sensor data.", e);
}
updateSensorData(sds);
try {
int i = service.getSensorState();
updateSensorState(Sensor.SensorState.valueOf(i));
} catch (RemoteException e) {
Log.e(Constants.TAG, "Could not read sensor state.", e);
updateSensorState(Sensor.SensorState.NONE);
}
}
private void updateSensorStateAndData(Sensor.SensorState state, Sensor.SensorDataSet dataSet)
{
updateSensorState(state == null ? Sensor.SensorState.NONE : state);
updateSensorData(dataSet);
}
private void updateSensorState(Sensor.SensorState state) {
@@ -255,7 +160,6 @@ public class SensorStateActivity extends Activity {
utils.setUnknown(R.id.cadence_state_register);
utils.setUnknown(R.id.power_state_register);
utils.setUnknown(R.id.heart_rate_register);
utils.setUnknown(R.id.battery_level_register);
return;
}
@@ -302,18 +206,5 @@ public class SensorStateActivity extends Activity {
: Sensor.SensorState.NONE,
this));
}
if (sds.hasBatteryLevel() && sds.getBatteryLevel().hasValue()
&& sds.getBatteryLevel().getState() == Sensor.SensorState.SENDING) {
utils.setText(R.id.battery_level_register,
Integer.toString(sds.getBatteryLevel().getValue()));
} else {
utils.setText(R.id.battery_level_register,
SensorUtils.getStateAsString(
sds.hasBatteryLevel()
? sds.getBatteryLevel().getState()
: Sensor.SensorState.NONE,
this));
}
}
}
@@ -23,7 +23,6 @@ import com.google.android.apps.mytracks.services.sensors.ant.AntUtils;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
@@ -33,13 +32,11 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.provider.Settings;
@@ -53,11 +50,7 @@ import java.util.Set;
/**
* An activity that let's the user see and edit the settings.
*
* This activity has two entry points, "root" and "display" preference screen.
* If bundle.getString("open_settings_screen") is set to "display_settings_screen_key", then
* the "display" preference screen is shown, otherwise, the "root" display preference is shown.
*
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
@@ -65,22 +58,12 @@ public class SettingsActivity extends PreferenceActivity {
private BackupPreferencesListener backupListener;
private SharedPreferences preferences;
/** Called when the activity is first created. */
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
initActivityCommons();
// If we only need the display setting screen nothing else needs to load.
if (processIntent())
return;
initActivitySpecifics();
}
private void initActivityCommons() {
// The volume we want to control is the Text-To-Speech volume
ApiFeatures apiFeatures = ApiFeatures.getInstance();
int volumeStream =
@@ -100,19 +83,6 @@ public class SettingsActivity extends PreferenceActivity {
// Load the preferences to be displayed
addPreferencesFromResource(R.xml.preferences);
// Disable TTS announcement preference if not available
if (!apiFeatures.hasTextToSpeech()) {
IntegerListPreference announcementFrequency =
(IntegerListPreference) findPreference(
getString(R.string.announcement_frequency_key));
announcementFrequency.setEnabled(false);
announcementFrequency.setValue("-1");
announcementFrequency.setSummary(
R.string.settings_not_available_summary);
}
}
private void initActivitySpecifics() {
// Hook up switching of displayed list entries between metric and imperial
// units
CheckBoxPreference metricUnitsPreference =
@@ -131,8 +101,18 @@ public class SettingsActivity extends PreferenceActivity {
updatePreferenceUnits(metricUnitsPreference.isChecked());
customizeSensorOptionsPreferences();
customizeTrackColorModePreferences();
// Disable TTS announcement preference if not available
if (!apiFeatures.hasTextToSpeech()) {
IntegerListPreference announcementFrequency =
(IntegerListPreference) findPreference(
getString(R.string.announcement_frequency_key));
announcementFrequency.setEnabled(false);
announcementFrequency.setValue("-1");
announcementFrequency.setSummary(
R.string.settings_not_available_summary);
}
// Hook up action for resetting all settings
Preference resetPreference = findPreference(getString(R.string.reset_key));
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -142,50 +122,6 @@ public class SettingsActivity extends PreferenceActivity {
return true;
}
});
// Add a confirmation dialog for the "Allow access" preference.
final CheckBoxPreference allowAccessPreference = (CheckBoxPreference) findPreference(
getString(R.string.allow_access_key));
allowAccessPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if ((Boolean) newValue) {
AlertDialog dialog = new AlertDialog.Builder(SettingsActivity.this)
.setCancelable(true)
.setTitle(getString(R.string.settings_allow_access))
.setMessage(getString(R.string.settings_allow_access_dialog_message))
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int button) {
allowAccessPreference.setChecked(true);
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.show();
return false;
} else {
return true;
}
}
});
}
private boolean processIntent() {
boolean showDisplaySettings = false;
Bundle bundle = getIntent().getExtras();
PreferenceScreen preferenceScreen;
String intentString = getString(R.string.open_settings_screen);
if (bundle != null) {
preferenceScreen = (PreferenceScreen) findPreference(bundle.getString(intentString));
if (preferenceScreen != null) {
showDisplaySettings = true;
setPreferenceScreen(preferenceScreen);
}
}
return showDisplaySettings;
}
private void customizeSensorOptionsPreferences() {
@@ -234,37 +170,10 @@ public class SettingsActivity extends PreferenceActivity {
sensorOptionsScreen.removePreference(findPreference(getString(R.string.ant_options_key)));
}
}
private void customizeTrackColorModePreferences() {
ListPreference trackColorModePreference =
(ListPreference) findPreference(getString(R.string.track_color_mode_key));
trackColorModePreference.setOnPreferenceChangeListener(
new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
updateTrackColorModeSettings((String) newValue);
return true;
}
});
updateTrackColorModeSettings(trackColorModePreference.getValue());
setTrackColorModePreferenceListeners();
PreferenceCategory speedOptionsCategory =
(PreferenceCategory) findPreference(getString(R.string.track_color_mode_fixed_speed_options_key));
speedOptionsCategory.removePreference(findPreference(getString(R.string.track_color_mode_fixed_speed_slow_key)));
speedOptionsCategory.removePreference(findPreference(getString(R.string.track_color_mode_fixed_speed_medium_key)));
}
@Override
protected void onResume() {
super.onResume();
// If we only need the display setting screen nothing else needs to load.
if (processIntent())
return;
configureBluetoothPreferences();
Preference backupNowPreference =
@@ -342,18 +251,6 @@ public class SettingsActivity extends PreferenceActivity {
}
}
private void updateTrackColorModeSettings(String trackColorMode) {
boolean usesFixedSpeed = trackColorMode.equals(getString(R.string.track_color_mode_fixed));
boolean usesDynamicSpeed = trackColorMode.equals(getString(R.string.track_color_mode_dynamic));
findPreference(
getString(R.string.track_color_mode_fixed_speed_slow_display_key)).setEnabled(usesFixedSpeed);
findPreference(
getString(R.string.track_color_mode_fixed_speed_medium_display_key)).setEnabled(usesFixedSpeed);
findPreference(
getString(R.string.track_color_mode_dynamic_speed_variation_key)).setEnabled(usesDynamicSpeed);
}
/**
* Updates all the preferences which give options with distance units to use
* the proper unit the user has selected.
@@ -482,84 +379,4 @@ public class SettingsActivity extends PreferenceActivity {
}
}.start();
}
/**
* Set the given edit text preference text.
* If the units are not metric convert the value before displaying.
*/
private void viewTrackColorModeSettings(EditTextPreference preference, int id) {
CheckBoxPreference metricUnitsPreference = (CheckBoxPreference) findPreference(
getString(R.string.metric_units_key));
if(metricUnitsPreference.isChecked()) {
return;
}
// Convert miles/h to km/h
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
String metricspeed = prefs.getString(getString(id), null);
int englishspeed;
try {
englishspeed = (int) (Double.parseDouble(metricspeed) * UnitConversions.KMH_TO_MPH);
} catch (NumberFormatException e) {
englishspeed = 0;
}
preference.getEditText().setText(String.valueOf(englishspeed));
}
/**
* Saves the given edit text preference value.
* If the units are not metric convert the value before saving.
*/
private void validateTrackColorModeSettings(EditTextPreference preference,
String newValue, int id) {
CheckBoxPreference metricUnitsPreference = (CheckBoxPreference) findPreference(
getString(R.string.metric_units_key));
String metricspeed;
if(!metricUnitsPreference.isChecked()) {
// Convert miles/h to km/h
try {
metricspeed = String.valueOf((int) (Double.parseDouble(newValue) * UnitConversions.MPH_TO_KMH) + 1);
} catch (NumberFormatException e) {
metricspeed = "0";
}
} else {
metricspeed = newValue;
}
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.edit().putString(getString(id), metricspeed).commit();
}
/**
* Sets the TrackColorMode preference listeners.
*/
private void setTrackColorModePreferenceListeners() {
setTrackColorModePreferenceListener(R.string.track_color_mode_fixed_speed_slow_display_key,
R.string.track_color_mode_fixed_speed_slow_key);
setTrackColorModePreferenceListener(R.string.track_color_mode_fixed_speed_medium_display_key,
R.string.track_color_mode_fixed_speed_medium_key);
}
/**
* Sets a TrackColorMode preference listener.
*/
private void setTrackColorModePreferenceListener(int displayKey, final int metricKey) {
EditTextPreference trackColorModePreference =
(EditTextPreference) findPreference(getString(displayKey));
trackColorModePreference.setOnPreferenceChangeListener(
new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
validateTrackColorModeSettings((EditTextPreference) preference, (String) newValue, metricKey);
return true;
}
});
trackColorModePreference.setOnPreferenceClickListener(
new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
viewTrackColorModeSettings((EditTextPreference) preference, metricKey);
return true;
}
});
}
}
@@ -137,7 +137,7 @@ public class StatsActivity extends Activity implements TrackDataListener {
protected void onResume() {
super.onResume();
dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
dataHub = TrackDataHub.getStartedInstance();
dataHub.registerTrackDataListener(this, EnumSet.of(
ListenerDataType.SELECTED_TRACK_CHANGED,
ListenerDataType.TRACK_UPDATES,
@@ -28,7 +28,6 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
@@ -140,7 +139,6 @@ public class WaypointDetails extends Activity
utils.setAllStats(waypointStats);
utils.setAltitude(
R.id.elevation_register, waypoint.getLocation().getAltitude());
name.setImeOptions(EditorInfo.IME_ACTION_DONE);
break;
}
icon.setImageDrawable(getResources().getDrawable(iconId));
@@ -17,13 +17,11 @@ package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.maps.mytracks.R;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
@@ -32,8 +30,6 @@ import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
@@ -187,22 +183,8 @@ public class MyTracksProvider extends ContentProvider {
"waypoints/#", WAYPOINTS_ID);
}
private boolean canAccess() {
if (Binder.getCallingPid() == Process.myPid()) {
return true;
} else {
Context context = getContext();
SharedPreferences sharedPreferences = context.getSharedPreferences(
Constants.SETTINGS_NAME, 0);
return sharedPreferences.getBoolean(context.getString(R.string.allow_access_key), false);
}
}
@Override
public boolean onCreate() {
if (!canAccess()) {
return false;
}
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
try {
db = dbHelper.getWritableDatabase();
@@ -214,9 +196,6 @@ public class MyTracksProvider extends ContentProvider {
@Override
public int delete(Uri url, String where, String[] selectionArgs) {
if (!canAccess()) {
return 0;
}
String table;
boolean shouldVacuum = false;
switch (urlMatcher.match(url)) {
@@ -249,9 +228,6 @@ public class MyTracksProvider extends ContentProvider {
@Override
public String getType(Uri url) {
if (!canAccess()) {
return null;
}
switch (urlMatcher.match(url)) {
case TRACKPOINTS:
return TrackPointsColumns.CONTENT_TYPE;
@@ -272,9 +248,6 @@ public class MyTracksProvider extends ContentProvider {
@Override
public Uri insert(Uri url, ContentValues initialValues) {
if (!canAccess()) {
return null;
}
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insert");
ContentValues values;
if (initialValues != null) {
@@ -303,9 +276,6 @@ public class MyTracksProvider extends ContentProvider {
@Override
public int bulkInsert(Uri url, ContentValues[] valuesBulk) {
if (!canAccess()) {
return 0;
}
Log.d(MyTracksProvider.TAG, "MyTracksProvider.bulkInsert");
int numInserted = 0;
try {
@@ -378,9 +348,6 @@ public class MyTracksProvider extends ContentProvider {
public Cursor query(
Uri url, String[] projection, String selection, String[] selectionArgs,
String sort) {
if (!canAccess()) {
return null;
}
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
int match = urlMatcher.match(url);
String sortOrder = null;
@@ -432,9 +399,6 @@ public class MyTracksProvider extends ContentProvider {
@Override
public int update(Uri url, ContentValues values, String where,
String[] selectionArgs) {
if (!canAccess()) {
return 0;
}
int count;
int match = urlMatcher.match(url);
if (match == TRACKPOINTS) {
@@ -195,12 +195,15 @@ public class TrackDataHub {
private int lastSamplingFrequency;
private DoubleBufferedLocationFactory locationFactory;
private boolean started = false;
private static TrackDataHub startedInstance;
/**
* Builds a new {@link TrackDataHub} instance.
*/
public synchronized static TrackDataHub newInstance(Context context) {
// Ensure our singleton is never bound to an activity, to avoid memory leaks.
context = context.getApplicationContext();
SharedPreferences preferences = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory.get(context);
return new TrackDataHub(context,
@@ -237,11 +240,11 @@ public class TrackDataHub {
*/
public void start() {
Log.i(TAG, "TrackDataHub.start");
if (isStarted()) {
if (startedInstance != null) {
Log.w(TAG, "Already started, ignoring");
return;
}
started = true;
startedInstance = this;
listenerHandlerThread = new HandlerThread("trackDataContentThread");
listenerHandlerThread.start();
@@ -263,6 +266,20 @@ public class TrackDataHub {
return new DataSourcesWrapperImpl(context, preferences);
}
/**
* If there's an instance for which {@link start} has been called, returns it.
*
* @return the started instance
* @throws IllegalStateException if there isn't a started instance
*/
public static TrackDataHub getStartedInstance() {
if (startedInstance == null) {
throw new IllegalStateException("Data hub not started");
}
return startedInstance;
}
/**
* Stops listening to data sources and reporting the data to external
* listeners.
@@ -273,12 +290,12 @@ public class TrackDataHub {
Log.w(TAG, "Not started, ignoring");
return;
}
// Unregister internal listeners even if there are external listeners registered.
dataSourceManager.unregisterAllListeners();
listenerHandlerThread.getLooper().quit();
started = false;
startedInstance = null;
dataSources = null;
dataSourceManager = null;
@@ -287,7 +304,7 @@ public class TrackDataHub {
}
private boolean isStarted() {
return started;
return startedInstance != null;
}
@Override
@@ -26,23 +26,20 @@ import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
import com.google.android.apps.mytracks.io.gdata.GDataWrapper.QueryFunction;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.googleapis.MethodOverride;
import com.google.api.client.googleapis.MethodOverrideIntercepter;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.InputStreamContent;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.javanet.NetHttpTransport;
import com.google.api.client.util.Strings;
import android.app.Activity;
@@ -67,8 +64,6 @@ import java.util.Vector;
*/
public class SendToFusionTables implements Runnable {
private static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
/**
* Listener invoked when sending to fusion tables completes.
*/
@@ -81,13 +76,13 @@ public class SendToFusionTables implements Runnable {
/** The path for viewing a map visualization of a table. */
private static final String FUSIONTABLES_MAP =
"https://www.google.com/fusiontables/embedviz?" +
"http://www.google.com/fusiontables/embedviz?" +
"viz=MAP&q=select+col0,+col1,+col2,+col3+from+%s+&h=false&" +
"lat=%f&lng=%f&z=%d&t=1&l=col2";
/** Standard base feed url for Fusion Tables. */
private static final String FUSIONTABLES_BASE_FEED_URL =
"https://www.google.com/fusiontables/api/query";
"http://www.google.com/fusiontables/api/query";
private static final int MAX_POINTS_PER_UPLOAD = 2048;
@@ -123,15 +118,22 @@ public class SendToFusionTables implements Runnable {
private int totalLocations;
private int totalSegmentsUploaded;
private HttpRequestFactory httpRequestFactory;
private HttpTransport transport;
private String tableId;
private static String MARKER_TYPE_START = "large_green";
private static String MARKER_TYPE_END = "large_red";
private static String MARKER_TYPE_WAYPOINT = "large_yellow";
public SendToFusionTables(Activity context, AuthManager auth, long trackId,
ProgressIndicator progressIndicator, OnSendCompletedListener onCompletion) {
static {
// We manually assign the transport to avoid having HttpTransport try to
// load it via reflection (which breaks due to ProGuard).
HttpTransport.setLowLevelHttpTransport(new NetHttpTransport());
}
public SendToFusionTables(Activity context, AuthManager auth,
long trackId, ProgressIndicator progressIndicator,
OnSendCompletedListener onCompletion) {
this.context = context;
this.auth = auth;
this.trackId = trackId;
@@ -140,9 +142,13 @@ public class SendToFusionTables implements Runnable {
this.stringUtils = new StringUtils(context);
this.providerUtils = MyTracksProviderUtils.Factory.get(context);
HttpTransport transport = ApiFeatures.getInstance().useNetHttpTransport()
? new NetHttpTransport() : new ApacheHttpTransport();
httpRequestFactory = transport.createRequestFactory(new MethodOverride());
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("Google-MyTracks-" + SystemUtils.getMyTracksVersion(context));
headers.gdataVersion = GDATA_VERSION;
transport = new HttpTransport();
MethodOverrideIntercepter.setAsFirstFor(transport);
transport.defaultHeaders = headers;
}
@Override
@@ -171,6 +177,7 @@ public class SendToFusionTables implements Runnable {
}
private void doUpload() {
((GoogleHeaders) transport.defaultHeaders).setGoogleLogin(auth.getAuthToken());
int statusMessageId = R.string.error_sending_to_fusiontables;
boolean success = true;
try {
@@ -273,7 +280,7 @@ public class SendToFusionTables implements Runnable {
*
* @param name the marker name
* @param description the marker description
* @param location the marker location
* @param the marker location
* @return true in case of success.
*/
private boolean createNewPoint(String name, String description, Location location,
@@ -299,6 +306,7 @@ public class SendToFusionTables implements Runnable {
}
private boolean uploadAllTrackPoints(final Track track, String originalDescription) {
SharedPreferences preferences = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
boolean metricUnits = true;
if (preferences != null) {
@@ -432,10 +440,10 @@ public class SendToFusionTables implements Runnable {
}
/**
* Returns a KML LineString.
* Returns a KML Point tag for the given location.
*
* @param track the track.
* @return the KML LineString.
* @param location The location.
* @return the kml.
*/
private String getKmlLineString(Track track) {
StringBuilder builder = new StringBuilder("<LineString><coordinates>");
@@ -492,7 +500,7 @@ public class SendToFusionTables implements Runnable {
* Prepares a buffer of locations for transmission to google fusion tables.
*
* @param track the original track with meta data
* @param locations locations on the track
* @param buffer a buffer of locations on the track
* @return an array of tracks each with a sub section of the points in the
* original buffer
*/
@@ -657,40 +665,33 @@ public class SendToFusionTables implements Runnable {
* @return true in case of success
*/
private boolean runUpdate(final String query) {
GDataWrapper<HttpRequestFactory> wrapper = new GDataWrapper<HttpRequestFactory>();
GDataWrapper<HttpTransport> wrapper = new GDataWrapper<HttpTransport>();
wrapper.setAuthManager(auth);
wrapper.setRetryOnAuthFailure(true);
wrapper.setClient(httpRequestFactory);
wrapper.setClient(transport);
Log.d(Constants.TAG, "GData connection prepared: " + this.auth);
wrapper.runQuery(new QueryFunction<HttpRequestFactory>() {
wrapper.runQuery(new QueryFunction<HttpTransport>() {
@Override
public void query(HttpRequestFactory factory)
public void query(HttpTransport client)
throws IOException, GDataWrapper.ParseException, GDataWrapper.HttpException,
GDataWrapper.AuthenticationException {
HttpRequest request = transport.buildPostRequest();
request.headers.contentType = "application/x-www-form-urlencoded";
GenericUrl url = new GenericUrl(FUSIONTABLES_BASE_FEED_URL);
request.url = url;
InputStreamContent isc = new InputStreamContent();
String sql = "sql=" + URLEncoder.encode(query, "UTF-8");
ByteArrayInputStream inputStream = new ByteArrayInputStream(Strings.toBytesUtf8(sql));
InputStreamContent isc = new InputStreamContent(null, inputStream );
isc.inputStream = new ByteArrayInputStream(Strings.toBytesUtf8(sql));
request.content = isc;
HttpRequest request = factory.buildPostRequest(url, isc);
GoogleHeaders headers = new GoogleHeaders();
headers.setApplicationName("Google-MyTracks-" + SystemUtils.getMyTracksVersion(context));
headers.gdataVersion = GDATA_VERSION;
headers.setGoogleLogin(auth.getAuthToken());
headers.setContentType(CONTENT_TYPE);
request.setHeaders(headers);
Log.d(Constants.TAG, "Running update query " + url.toString() + ": " + sql);
HttpResponse response;
try {
response = request.execute();
} catch (HttpResponseException e) {
throw new GDataWrapper.HttpException(e.getResponse().getStatusCode(),
e.getResponse().getStatusMessage());
throw new GDataWrapper.HttpException(e.response.statusCode, e.response.statusMessage);
}
boolean success = response.isSuccessStatusCode();
boolean success = response.isSuccessStatusCode;
if (success) {
byte[] result = new byte[1024];
int read = response.getContent().read(result);
@@ -703,11 +704,9 @@ public class SendToFusionTables implements Runnable {
Log.w(Constants.TAG, "Unrecognized response: " + lines[0]);
}
} else {
Log.d(Constants.TAG,
"Query failed: " + response.getStatusMessage() + " ("
+ response.getStatusCode() + ")");
throw new GDataWrapper.HttpException(
response.getStatusCode(), response.getStatusMessage());
Log.d(Constants.TAG, "Query failed: " + response.statusMessage + " (" +
response.statusCode + ")");
throw new GDataWrapper.HttpException(response.statusCode, response.statusMessage);
}
}
});
@@ -34,7 +34,6 @@ import com.google.wireless.gdata.parser.ParseException;
import com.google.wireless.gdata2.client.AuthenticationException;
import android.content.Context;
import android.text.format.DateFormat;
import android.util.Log;
import java.io.BufferedReader;
@@ -45,7 +44,6 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -57,15 +55,15 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public class DocsHelper {
private static final String DOCS_FEED_URL =
"https://docs.google.com/feeds/documents/private/full";
"http://docs.google.com/feeds/documents/private/full";
private static final String DOCS_SPREADSHEET_URL =
"https://docs.google.com/feeds/documents/private/full/spreadsheet%3A";
"http://docs.google.com/feeds/documents/private/full/spreadsheet%3A";
private static final String DOCS_WORKSHEETS_URL_FORMAT =
"https://spreadsheets.google.com/feeds/worksheets/%s/private/full";
"http://spreadsheets.google.com/feeds/worksheets/%s/private/full";
private static final String DOCS_MY_SPREADSHEETS_FEED_URL =
"https://docs.google.com/feeds/documents/private/full?category=mine,spreadsheet";
"http://docs.google.com/feeds/documents/private/full?category=mine,spreadsheet";
private static final String DOCS_SPREADSHEET_URL_FORMAT =
"https://spreadsheets.google.com/feeds/list/%s/%s/private/full";
"http://spreadsheets.google.com/feeds/list/%s/%s/private/full";
private static final String CONTENT_TYPE_PARAM = "Content-Type";
private static final String OPENDOCUMENT_SPREADSHEET_MIME_TYPE =
@@ -287,16 +285,11 @@ public class DocsHelper {
String elevationUnit = context.getString(metricUnits ?
R.string.meter : R.string.feet);
java.text.DateFormat dateFormat = DateFormat.getDateFormat(context);
java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context);
Date startTime = new Date(stats.getStartTime());
String dateString = dateFormat.format(startTime) + " " + timeFormat.format(startTime);
// Prepare the Post-Text we are going to send.
DocsTagBuilder tagBuilder = new DocsTagBuilder(metricUnits)
.append("name", track.getName())
.append("description", track.getDescription())
.append("date", dateString)
.append("date", String.format("%tc", stats.getStartTime()))
.append("totaltime", StringUtils.formatTimeAlwaysShowingHours(
stats.getTotalTime()))
.append("movingtime", StringUtils.formatTimeAlwaysShowingHours(
@@ -15,44 +15,40 @@
*/
package com.google.android.apps.mytracks.io.docs;
import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
/**
* <p>This class builds a string of XML tags used to talk to Docs using GData.
*
*
* <p>Sample Usage:
*
*
* <code>
* String tags = new DocsTagBuilder(false)
* .append("tagName", "tagValue")
* .appendLargeUnits("bigTagName", 1.0)
* .build();
* </code>
*
*
* <p>results in:
*
*
* <code>
* <gsx:tagName><![CDATA[tagValue]]></gsx:tagName>
* <gsx:bigTagName><![CDATA[1.00]]></gsx:bigTagName>
* </code>
*
*
* @author Matthew Simmons
*/
class DocsTagBuilder {
private static final DecimalFormatSymbols FORMAT_SYMBOLS =
ApiFeatures.getInstance().getApiPlatformAdapter().getDecimalFormatSymbols(Locale.ENGLISH);
// TODO(simmonmt): These formats aren't I18N-compatible. Not everyone uses
// commas for thousands and dots for the decimal point.
private static final NumberFormat LARGE_UNIT_FORMAT =
new DecimalFormat("#,###,###.00", FORMAT_SYMBOLS);
new DecimalFormat("#,###,###.00");
private static final NumberFormat SMALL_UNIT_FORMAT =
new DecimalFormat("###,###", FORMAT_SYMBOLS);
new DecimalFormat("###,###");
protected final boolean metricUnits;
protected final StringBuilder stringBuilder;
@@ -63,7 +59,7 @@ class DocsTagBuilder {
*/
DocsTagBuilder(boolean metricUnits) {
this.metricUnits = metricUnits;
this.stringBuilder = new StringBuilder();
stringBuilder = new StringBuilder();
}
/** Appends a tag containing a string value */
@@ -72,13 +68,13 @@ class DocsTagBuilder {
return this;
}
/**
/**
* Appends a tag containing a numeric value. The value will be formatted
* according to the large distance of the specified measurement system (i.e.
* kilometers or miles).
*
*
* @param name The tag name.
* @param d The value to be formatted, in kilometers.
* @param d The value to be formatted, in kilometers.
*/
DocsTagBuilder appendLargeUnits(String name, double d) {
double value = metricUnits ? d : (d * UnitConversions.KM_TO_MI);
@@ -90,7 +86,7 @@ class DocsTagBuilder {
* Appends a tag containing a numeric value. The value will be formatted
* according to the small distance of the specified measurement system (i.e.
* meters or feet).
*
*
* @param name The tag name.
* @param d The value to be formatted, in meters.
*/
@@ -44,7 +44,6 @@ import java.util.TimeZone;
* Garmin Training Center 3.5.3.
*
* @author Sandor Dornbush
* @author Dominik Ršttsches
*/
public class TcxTrackWriter implements TrackFormatWriter {
protected static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
@@ -65,9 +64,6 @@ public class TcxTrackWriter implements TrackFormatWriter {
private PrintWriter pw = null;
private Track track;
// Determines whether to encode cadence value as running or cycling cadence.
private boolean sportIsCycling;
public TcxTrackWriter(Context context) {
this.context = context;
@@ -80,7 +76,7 @@ public class TcxTrackWriter implements TrackFormatWriter {
public void prepare(Track track, OutputStream out) {
this.track = track;
this.pw = new PrintWriter(out);
this.sportIsCycling = categoryToTcxSport(track.getCategory()).equals(TCX_SPORT_BIKING);
}
@Override
@@ -178,46 +174,23 @@ public class TcxTrackWriter implements TrackFormatWriter {
pw.print("</Value>");
pw.println("</HeartRateBpm>");
}
boolean cadenceAvailable = sensorData.hasCadence()
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
&& sensorData.getCadence().hasValue();
// TCX Trackpoint_t contains a sequence. Thus, the legacy XML element
// <Cadence> needs to be put before <Extensions>.
// This field should only be used for the case that activity was marked as biking.
// Otherwise cadence is interpreted as running cadence data which
// is written in the <Extensions> as <RunCadence>.
if (sportIsCycling && cadenceAvailable) {
if (sensorData.hasPower()
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
&& sensorData.getPower().hasValue()) {
pw.print(" <Extensions>");
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
pw.print("<Watts>");
pw.print(sensorData.getPower().getValue());
pw.print("</Watts>");
pw.println("</TPX></Extensions>");
}
if (sensorData.hasCadence()
&& sensorData.getCadence().getState() == Sensor.SensorState.SENDING
&& sensorData.getCadence().hasValue()) {
pw.print(" <Cadence>");
pw.print(Math.min(254, sensorData.getCadence().getValue()));
pw.println("</Cadence>");
}
boolean powerAvailable = sensorData.hasPower()
&& sensorData.getPower().getState() == Sensor.SensorState.SENDING
&& sensorData.getPower().hasValue();
if(powerAvailable || (!sportIsCycling && cadenceAvailable)) {
pw.print(" <Extensions>");
pw.print("<TPX xmlns=\"http://www.garmin.com/xmlschemas/ActivityExtension/v2\">");
// RunCadence needs to be put before power in order to be understood
// by Garmin Training Center.
if (!sportIsCycling && cadenceAvailable) {
pw.print("<RunCadence>");
pw.print(Math.min(254, sensorData.getCadence().getValue()));
pw.print("</RunCadence>");
}
if (powerAvailable) {
pw.print("<Watts>");
pw.print(sensorData.getPower().getValue());
pw.print("</Watts>");
}
pw.println("</TPX></Extensions>");
}
}
}
pw.println(" </Trackpoint>");
@@ -243,9 +216,11 @@ public class TcxTrackWriter implements TrackFormatWriter {
// TCX schema.
pw.println("<UnitId>0</UnitId>");
pw.println("<ProductID>0</ProductID>");
writeVersion();
pw.println("<Version>");
pw.println("<VersionMajor>0</VersionMajor>");
pw.println("<VersionMinor>0</VersionMinor>");
pw.println("</Version>");
pw.println("</Creator>");
pw.println(" </Activity>");
pw.println(" </Activities>");
@@ -266,7 +241,15 @@ public class TcxTrackWriter implements TrackFormatWriter {
pw.println("<Build>");
writeVersion();
// The TCX spec for the Version tag is too strict to allow us to use our
// user-visible version number, so we use our version code instead. We
// also set the minor version to 1 if this is a development build to
// signify that this build is newer than the one associated with the
// version code given in VersionMajor.
pw.println("<Version>");
pw.format("<VersionMajor>%s</VersionMajor>\n", SystemUtils.getMyTracksVersion(context));
pw.format("<VersionMinor>%d</VersionMinor>\n", SystemUtils.isRelease(context) ? 0 : 1);
pw.println("</Version>");
pw.format("<Type>%s</Type>\n", SystemUtils.isRelease(context) ? TCX_TYPE_RELEASE
: TCX_TYPE_INTERNAL);
@@ -281,30 +264,6 @@ public class TcxTrackWriter implements TrackFormatWriter {
public void writeWaypoint(Waypoint waypoint) {
// TODO Write out the waypoints somewhere.
}
private void writeVersion() {
if (pw == null) {
return;
}
// Splitting the myTracks version code into VersionMajor, VersionMinor and BuildMajor
// to fit the integer type requirement for these fields in the TCX spec.
// Putting a string like "x.x.x" into VersionMajor breaks XML validation.
// We also set the BuildMinor version to 1 if this is a development build to
// signify that this build is newer than the one associated with the
// version code given in BuildMajor.
String[] myTracksVersionComponents = SystemUtils.getMyTracksVersion(context).split("\\.");
pw.println("<Version>");
pw.format("<VersionMajor>%d</VersionMajor>\n", Integer.valueOf(myTracksVersionComponents[0]));
pw.format("<VersionMinor>%d</VersionMinor>\n", Integer.valueOf(myTracksVersionComponents[1]));
// TCX schema says these are optional but http://connect.garmin.com only accepts
// the TCX file when they are present.
pw.format("<BuildMajor>%d</BuildMajor>\n", Integer.valueOf(myTracksVersionComponents[2]));
pw.format("<BuildMinor>%d</BuildMinor>\n", SystemUtils.isRelease(context) ? 0 : 1);
pw.println("</Version>");
}
private String categoryToTcxSport(String category) {
category = category.trim();
@@ -70,11 +70,6 @@ class WriteProgressController {
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
// For testing purpose
OnCompletionListener getOnCompletionListener() {
return onCompletionListener;
}
public ProgressDialog createProgressDialog() {
dialog = new ProgressDialog(activity);
@@ -7,7 +7,7 @@ package com.google.android.apps.mytracks.io.mymaps;
public class MyMapsConstants {
static final String TAG = "MapsApi";
static final String MAPSHOP_BASE_URL =
"https://maps.google.com/maps/ms";
"http://maps.google.com/maps/ms";
public static final String SERVICE_NAME = "local";
/**
@@ -1,129 +0,0 @@
/*
* Copyright 2011 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.maps;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.util.Log;
/**
* A dynamic speed path descriptor.
*
* @author Vangelis S.
*/
public class DynamicSpeedTrackPathDescriptor
implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
private int slowSpeed;
private int normalSpeed;
private int speedMargin;
private double averageMovingSpeed;
private final Context context;
public DynamicSpeedTrackPathDescriptor(Context context){
this.context = context;
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
speedMargin = 25;
return;
}
prefs.registerOnSharedPreferenceChangeListener(this);
speedMargin = Integer.parseInt(prefs.getString(
context.getString(R.string.track_color_mode_dynamic_speed_variation_key), "25"));
}
/**
* Get the slow speed calculated based on the % below the average speed.
* @return The speed limit considered as slow.
*/
public int getSlowSpeed()
{
slowSpeed = (int) (averageMovingSpeed - (averageMovingSpeed * speedMargin / 100));
return slowSpeed;
}
/**
* Get the medium speed calculated based on the % above the average speed.
* @return The speed limit considered as normal.
*/
public int getNormalSpeed()
{
normalSpeed = (int) (averageMovingSpeed + (averageMovingSpeed * speedMargin / 100));
return normalSpeed;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "DynamicSpeedTrackPathDescriptor: onSharedPreferences changed " + key);
if (key == null
|| !key.equals(context.getString(R.string.track_color_mode_dynamic_speed_variation_key))) {
return;
}
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
speedMargin = 25;
return;
}
speedMargin =
Integer.parseInt(
prefs.getString(
context.getString(R.string.track_color_mode_dynamic_speed_variation_key), "25"));
}
@Override
public boolean needsRedraw() {
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
long currentTrackId = prefs.getLong(context.getString(R.string.selected_track_key), -1);
if(currentTrackId == -1) {
// Could not find track.
return false;
}
Track track = MyTracksProviderUtils.Factory.get(context).getTrack(currentTrackId);
TripStatistics stats = track.getStatistics();
double newaverageSpeed = (int) Math.floor(stats.getAverageMovingSpeed() * 3.6);
if(averageMovingSpeed == 0) {
averageMovingSpeed = newaverageSpeed;
return true;
}
double difference = Math.max(averageMovingSpeed, newaverageSpeed);
if (difference == 0.0) {
difference = 0.0;
} else {
difference = Math.abs(averageMovingSpeed - newaverageSpeed) / difference * 100;
}
if(difference >= 20) {
averageMovingSpeed = newaverageSpeed;
return true;
}
return false;
}
}
@@ -1,141 +0,0 @@
/*
* Copyright 2011 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.maps;
import com.google.android.apps.mytracks.ColoredPath;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.Projection;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import java.util.ArrayList;
import java.util.List;
/**
* A path painter that varies the path colors based on fixed speeds or average speed margin
* depending of the TrackPathDescriptor passed to its constructor.
*
* @author Vangelis S.
*/
public class DynamicSpeedTrackPathPainter implements TrackPathPainter {
private final Paint selectedTrackPaintSlow;
private final Paint selectedTrackPaintMedium;
private final Paint selectedTrackPaintFast;
private final List<ColoredPath> coloredPaths;
private final TrackPathDescriptor trackPathDescriptor;
private int slowSpeed;
private int normalSpeed;
public DynamicSpeedTrackPathPainter (Context context, TrackPathDescriptor trackPathDescriptor) {
this.trackPathDescriptor = trackPathDescriptor;
selectedTrackPaintSlow = TrackPathUtilities.getPaint(R.color.slow_path, context);
selectedTrackPaintMedium = TrackPathUtilities.getPaint(R.color.normal_path, context);
selectedTrackPaintFast = TrackPathUtilities.getPaint(R.color.fast_path, context);
this.coloredPaths = new ArrayList<ColoredPath>();
}
@Override
public void drawTrack(Canvas canvas) {
for(int i = 0; i < coloredPaths.size(); ++i) {
ColoredPath coloredPath = coloredPaths.get(i);
canvas.drawPath(coloredPath.getPath(), coloredPath.getPathPaint());
}
}
@Override
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
Boolean alwaysVisible, List<CachedLocation> points) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment = startLocationIdx <= 0 || !points.get(startLocationIdx - 1).valid;
boolean lastVisible = !newSegment;
final Point pt = new Point();
clear();
slowSpeed = trackPathDescriptor.getSlowSpeed();
normalSpeed = trackPathDescriptor.getNormalSpeed();
// Loop over track points.
for (int i = startLocationIdx; i < points.size(); ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible || viewRect.contains(
geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
if (newSegment) {
projection.toPixels(geoPoint, pt);
newSegment = false;
} else {
ColoredPath coloredPath;
if(loc.speed <= slowSpeed) {
coloredPath = new ColoredPath(selectedTrackPaintSlow);
}
else if(loc.speed <= normalSpeed) {
coloredPath = new ColoredPath(selectedTrackPaintMedium);
} else {
coloredPath = new ColoredPath(selectedTrackPaintFast);
}
coloredPath.getPath().moveTo(pt.x, pt.y);
projection.toPixels(geoPoint, pt);
coloredPath.getPath().lineTo(pt.x, pt.y);
coloredPaths.add(coloredPath);
}
}
}
@Override
public void clear()
{
coloredPaths.clear();
}
@Override
public boolean needsRedraw() {
return trackPathDescriptor.needsRedraw();
}
@Override
public Path getLastPath() {
Path path = new Path();
for(int i = 0; i < coloredPaths.size(); ++i) {
path.addPath(coloredPaths.get(i).getPath());
}
return path;
}
}
@@ -1,115 +0,0 @@
/*
* Copyright 2011 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.maps;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.util.Log;
/**
* A fixed speed path descriptor.
*
* @author Vangelis S.
*/
public class FixedSpeedTrackPathDescriptor implements TrackPathDescriptor, OnSharedPreferenceChangeListener {
private int slowSpeed;
private int normalSpeed;
private final Context context;
public FixedSpeedTrackPathDescriptor(Context context) {
this.context = context;
context.getSharedPreferences(Constants.SETTINGS_NAME, 0)
.registerOnSharedPreferenceChangeListener(this);
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
slowSpeed = 9;
normalSpeed = 17;
return;
}
try {
slowSpeed = Integer.parseInt(prefs.getString(context.getString(
R.string.track_color_mode_fixed_speed_slow_key), "9"));
} catch (NumberFormatException e) {
slowSpeed = 9;
}
try {
normalSpeed = Integer.parseInt(prefs.getString(context.getString(
R.string.track_color_mode_fixed_speed_medium_key), "17"));
} catch (NumberFormatException e) {
normalSpeed = 17;
}
}
/**
* Gets the slow speed for reference.
* @return The speed limit considered as slow.
*/
public int getSlowSpeed() {
return slowSpeed;
}
/**
* Gets the normal speed for reference.
* @return The speed limit considered as normal.
*/
public int getNormalSpeed() {
return normalSpeed;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "FixedSpeedTrackPathDescriptor: onSharedPreferences changed " + key);
if (key == null
|| (!key.equals(context.getString(R.string.track_color_mode_fixed_speed_slow_key))
&& !key.equals(context.getString(R.string.track_color_mode_fixed_speed_medium_key)))) {
return;
}
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
slowSpeed = 9;
normalSpeed = 17;
return;
}
try {
slowSpeed = Integer.parseInt(prefs.getString(context.getString(
R.string.track_color_mode_fixed_speed_slow_key), "9"));
} catch (NumberFormatException e) {
slowSpeed = 9;
}
try {
normalSpeed = Integer.parseInt(prefs.getString(context.getString(
R.string.track_color_mode_fixed_speed_medium_key), "17"));
} catch (NumberFormatException e) {
normalSpeed = 17;
}
}
@Override
public boolean needsRedraw() {
return false;
}
}
@@ -1,112 +0,0 @@
/*
* Copyright 2011 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.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.Projection;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import java.util.List;
/**
* A path painter that not variates the path colors.
*
* @author Vangelis S.
*/
public class SingleColorTrackPathPainter implements TrackPathPainter {
private final Paint selectedTrackPaint;
private Path path;
public SingleColorTrackPathPainter(Context context) {
selectedTrackPaint = TrackPathUtilities.getPaint(R.color.red, context);
}
@Override
public void drawTrack(Canvas canvas) {
canvas.drawPath(path, selectedTrackPaint);
}
@Override
public void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
Boolean alwaysVisible, List<CachedLocation> points) {
// Whether to start a new segment on new valid and visible point.
boolean newSegment = startLocationIdx <= 0 || !points.get(startLocationIdx - 1).valid;
boolean lastVisible = !newSegment;
final Point pt = new Point();
// Loop over track points.
int numPoints = points.size();
path = newPath();
path.incReserve(numPoints);
for (int i = startLocationIdx; i < numPoints ; ++i) {
CachedLocation loc = points.get(i);
// Check if valid, if not then indicate a new segment.
if (!loc.valid) {
newSegment = true;
continue;
}
final GeoPoint geoPoint = loc.geoPoint;
// Check if this breaks the existing segment.
boolean visible = alwaysVisible
|| viewRect.contains(geoPoint.getLongitudeE6(), geoPoint.getLatitudeE6());
if (!visible && !lastVisible) {
// This is a point outside view not connected to a visible one.
newSegment = true;
}
lastVisible = visible;
// Either move to beginning of a new segment or continue the old one.
projection.toPixels(geoPoint, pt);
if (newSegment) {
path.moveTo(pt.x, pt.y);
newSegment = false;
} else {
path.lineTo(pt.x, pt.y);
}
}
}
@Override
public void clear() {
path = null;
}
@Override
public boolean needsRedraw() {
return false;
}
@Override
public Path getLastPath() {
return path;
}
// Visible for testing
public Path newPath() {
return new Path();
}
}
@@ -1,38 +0,0 @@
/*
* Copyright 2011 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.maps;
/**
* An interface for classes which describe how to draw a track path.
*
* @author Vangelis S.
*/
public interface TrackPathDescriptor {
/**
* @return The maximum speed which is considered slow.
*/
int getSlowSpeed();
/**
* @return The maximum speed which is considered normal.
*/
int getNormalSpeed();
/**
* @return True if the path needs to be updated.
*/
boolean needsRedraw();
}
@@ -1,68 +0,0 @@
/*
* Copyright 2011 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.maps;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.maps.Projection;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import java.util.List;
/**
* An interface for classes which paint the track path.
*
* @author Vangelis S.
*/
public interface TrackPathPainter {
/**
* Clears the related data.
*/
void clear();
/**
* Draws the path to the canvas.
* @param canvas The Canvas to draw upon
*/
void drawTrack(Canvas canvas);
/**
* Updates the path.
* @param projection The Canvas to draw upon.
* @param viewRect The Path to be drawn.
* @param startLocationIdx The start point from where update the path.
* @param alwaysVisible Flag for alwaysvisible.
* @param points The list of points used to update the path.
*/
void updatePath(Projection projection, Rect viewRect, int startLocationIdx,
Boolean alwaysVisible, List<CachedLocation> points);
/**
* @return True if the path needs to be updated.
*/
boolean needsRedraw();
/**
* @return The path being used currently.
*/
Path getLastPath();
}
@@ -1,65 +0,0 @@
/*
* Copyright 2011 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.maps;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
/**
* A factory for TrackPathPainters.
*
* @author Vangelis S.
*/
public class TrackPathPainterFactory {
private TrackPathPainterFactory() {
}
/**
* Get a new TrackPathPainter.
* @param context Context to fetch system preferences.
* @return The TrackPathPainter that corresponds to the track color mode setting.
*/
public static TrackPathPainter getTrackPathPainter(Context context) {
SharedPreferences prefs =
context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
return new SingleColorTrackPathPainter(context);
}
String colorMode = prefs.getString(context.getString(R.string.track_color_mode_key), null);
Log.i(TAG, "Creating track path painter of type: " + colorMode);
if (colorMode == null || colorMode.equals(context.getString(R.string.track_color_mode_none))) {
return new SingleColorTrackPathPainter(context);
} else if (colorMode.equals(context.getString(R.string.track_color_mode_fixed))) {
return new DynamicSpeedTrackPathPainter(context,
new FixedSpeedTrackPathDescriptor(context));
} else if (colorMode.equals(context.getString(R.string.track_color_mode_dynamic))) {
return new DynamicSpeedTrackPathPainter(context,
new DynamicSpeedTrackPathDescriptor(context));
} else {
Log.w(TAG, "Using default track path painter. Unrecognized painter: " + colorMode);
return new SingleColorTrackPathPainter(context);
}
}
}
@@ -1,36 +0,0 @@
/*
* Copyright 2011 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.maps;
import android.content.Context;
import android.graphics.Paint;
/**
* Various utility functions for TrackPath painting.
*
* @author Vangelis S.
*/
public class TrackPathUtilities {
public static Paint getPaint(int id, Context context) {
Paint paint = new Paint();
paint.setColor(context.getResources().getColor(id));
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
return paint;
}
}
@@ -1,121 +0,0 @@
/*
* Copyright 2011 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;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
import com.google.android.maps.mytracks.R;
import android.app.IntentService;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* A service to control starting and stopping of a recording. This service,
* through the AndroidManifest.xml, is configured to only allow components of
* the same application to invoke it. Thus this service can be used my MyTracks
* app widget, {@link TrackWidgetProvider}, but not by other applications. This
* application delegates starting and stopping a recording to
* {@link TrackRecordingService} using RPC calls.
*
* @author jshih@google.com (Jimmy Shih)
*/
public class ControlRecordingService extends IntentService implements ServiceConnection {
private ITrackRecordingService trackRecordingService;
private boolean connected = false;
public ControlRecordingService() {
super(ControlRecordingService.class.getSimpleName());
}
@Override
public void onCreate() {
super.onCreate();
Intent newIntent = new Intent(this, TrackRecordingService.class);
startService(newIntent);
bindService(newIntent, this, 0);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
trackRecordingService = ITrackRecordingService.Stub.asInterface(service);
notifyConnected();
}
@Override
public void onServiceDisconnected(ComponentName name) {
connected = false;
}
/**
* Notifies all threads that connection to {@link TrackRecordingService} is
* available.
*/
private synchronized void notifyConnected() {
connected = true;
notifyAll();
}
/**
* Waits until the connection to {@link TrackRecordingService} is available.
*/
private synchronized void waitConnected() {
while (!connected) {
try {
wait();
} catch (InterruptedException e) {
// can safely ignore
}
}
}
@Override
protected void onHandleIntent(Intent intent) {
waitConnected();
String action = intent.getAction();
if (action != null) {
try {
if (action.equals(getString(R.string.start_new_track_action))) {
trackRecordingService.startNewTrack();
} else if (action.equals(getString(R.string.end_current_track_action))) {
trackRecordingService.endCurrentTrack();
}
} catch (RemoteException e) {
Log.d(TAG, "ControlRecordingService onHandleIntent RemoteException", e);
}
}
unbindService(this);
connected = false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (connected) {
unbindService(this);
connected = false;
}
}
}
@@ -56,13 +56,11 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.Process;
import android.util.Log;
import java.util.Timer;
@@ -155,9 +153,6 @@ public class TrackRecordingService extends Service {
@Override
public void onLocationChanged(final Location location) {
if (executorService.isShutdown() || executorService.isTerminated()) {
return;
}
executorService.submit(
new Runnable() {
@Override
@@ -231,13 +226,6 @@ public class TrackRecordingService extends Service {
* Application lifetime events:
*/
/*
* Note that this service, through the AndroidManifest.xml, is configured to
* allow both MyTracks and third party apps to invoke it. For the onCreate
* callback, we cannot tell whether the caller is MyTracks or a third party
* app, thus it cannot start/stop a recording or write/update MyTracks
* database. However, it can resume a recording.
*/
@Override
public void onCreate() {
super.onCreate();
@@ -277,48 +265,40 @@ public class TrackRecordingService extends Service {
showNotification();
}
/*
* Note that this service, through the AndroidManifest.xml, is configured to
* allow both MyTracks and third party apps to invoke it. For the onStart
* callback, we cannot tell whether the caller is MyTracks or a third party
* app, thus it cannot start/stop a recording or write/update MyTracks
* database. However, it can resume a recording.
*/
@Override
public void onStart(Intent intent, int startId) {
handleStartCommand(intent, startId);
}
/*
* Note that this service, through the AndroidManifest.xml, is configured to
* allow both MyTracks and third party apps to invoke it. For the
* onStartCommand callback, we cannot tell whether the caller is MyTracks or a
* third party app, thus it cannot start/stop a recording or write/update
* MyTracks database. However, it can resume a recording.
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleStartCommand(intent, startId);
return START_STICKY;
}
private void handleStartCommand(Intent intent, int startId) {
Log.d(TAG, "TrackRecordingService.handleStartCommand: " + startId);
if (intent == null) {
return;
}
private void handleStartCommand(Intent intent, int startId) {
Log.d(TAG,
"TrackRecordingService.handleStartCommand: " + startId);
// Check if called on phone reboot with resume intent.
if (intent.getBooleanExtra(RESUME_TRACK_EXTRA_NAME, false)) {
resumeTrack(startId);
if (intent != null) {
if (intent.getBooleanExtra(RESUME_TRACK_EXTRA_NAME, false)) {
resumeTrack(startId);
} else {
// Process actions for controlling the service.
String action = intent.getAction();
if (getString(R.string.start_new_track_action).equals(action)) {
startNewTrack();
if (intent.getBooleanExtra(getString(R.string.select_new_track_extra), false)) {
prefManager.setSelectedTrack(recordingTrackId);
}
} else if (getString(R.string.end_current_track_action).equals(action)) {
endCurrentTrack();
}
}
}
}
private boolean isTrackInProgress() {
return recordingTrackId != -1 || isRecording;
}
private void resumeTrack(int startId) {
Log.d(TAG, "TrackRecordingService: requested resume");
@@ -362,6 +342,7 @@ public class TrackRecordingService extends Service {
timer.purge();
unregisterLocationListener();
shutdownTaskExecutors();
executorService.shutdown();
if (sensorManager != null) {
sensorManager.shutdown();
sensorManager = null;
@@ -377,8 +358,6 @@ public class TrackRecordingService extends Service {
// This should be the last operation.
releaseWakeLock();
// Shutdown the executor service last to avoid sending events to a dead executor.
executorService.shutdown();
super.onDestroy();
}
@@ -564,10 +543,10 @@ public class TrackRecordingService extends Service {
* Recording lifecycle.
*/
private long startNewTrack() {
public long startNewTrack() {
Log.d(TAG, "TrackRecordingService.startNewTrack");
if (isTrackInProgress()) {
return -1L;
if (recordingTrackId != -1 || isRecording) {
throw new IllegalStateException("A track is already in progress!");
}
long startTime = System.currentTimeMillis();
@@ -975,7 +954,8 @@ public class TrackRecordingService extends Service {
* Build a statistics marker.
* A statistics marker holds the stats for the* last segment up to this marker.
*
* @param waypoint The waypoint which will be populated with stats data.
* @param Waypoint The waypoint which will be populated with stats data.
* @return the unique id of the inserted marker
*/
private void buildStatisticsMarker(Waypoint waypoint) {
StringUtils utils = new StringUtils(TrackRecordingService.this);
@@ -1003,8 +983,8 @@ public class TrackRecordingService extends Service {
private void endCurrentTrack() {
Log.d(TAG, "TrackRecordingService.endCurrentTrack");
if (!isTrackInProgress()) {
return;
if (recordingTrackId == -1 || !isRecording) {
throw new IllegalStateException("No recording track in progress!");
}
announcementExecutor.shutdown();
@@ -1045,15 +1025,12 @@ public class TrackRecordingService extends Service {
}
private void sendTrackBroadcast(int actionResId, long trackId) {
Intent broadcastIntent = new Intent()
.setAction(getString(actionResId))
.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
sendBroadcast(broadcastIntent, getString(R.string.mytracks_notifications_permission));
SharedPreferences sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (sharedPreferences.getBoolean(getString(R.string.allow_access_key), false)) {
sendBroadcast(broadcastIntent, getString(R.string.broadcast_notifications_permission));
}
Intent broadcastIntent =
new Intent()
.setAction(getString(actionResId))
.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
sendBroadcast(broadcastIntent,
getString(R.string.broadcast_notifications_permission));
}
/*
@@ -1185,58 +1162,29 @@ public class TrackRecordingService extends Service {
}
}
/**
* Checks if the service is available. If not, throws an
* {@link IllegalStateException}.
*/
private void checkService() {
if (service == null) {
throw new IllegalStateException("The service has been already detached!");
}
}
/**
* Returns true if the RPC caller is from the same application or if the
* "Allow access" setting indicates that another app can invoke this service's
* RPCs.
*/
private boolean canAccess() {
// As a precondition for access, must check if the service is available.
checkService();
if (Process.myPid() == Binder.getCallingPid()) {
return true;
} else {
SharedPreferences sharedPreferences = service.getSharedPreferences(
Constants.SETTINGS_NAME, 0);
return sharedPreferences.getBoolean(service.getString(R.string.allow_access_key), false);
}
}
// Service method delegates.
@Override
public boolean isRecording() {
if (!canAccess()) {
return false;
}
checkService();
return service.isRecording();
}
@Override
public long getRecordingTrackId() {
if (!canAccess()) {
return -1L;
}
checkService();
return service.recordingTrackId;
}
@Override
public long startNewTrack() {
if (!canAccess()) {
return -1L;
}
checkService();
return service.startNewTrack();
}
@@ -1247,33 +1195,25 @@ public class TrackRecordingService extends Service {
* @return the unique ID of the inserted marker
*/
public long insertWaypoint(WaypointCreationRequest request) {
if (!canAccess()) {
return -1L;
}
checkService();
return service.insertWaypoint(request);
}
@Override
public void endCurrentTrack() {
if (!canAccess()) {
return;
}
checkService();
service.endCurrentTrack();
}
@Override
public void recordLocation(Location loc) {
if (!canAccess()) {
return;
}
checkService();
service.locationListener.onLocationChanged(loc);
}
@Override
public byte[] getSensorData() {
if (!canAccess()) {
return null;
}
checkService();
if (service.sensorManager == null) {
Log.d(TAG, "No sensor manager for data.");
return null;
@@ -1287,9 +1227,7 @@ public class TrackRecordingService extends Service {
@Override
public int getSensorState() {
if (!canAccess()) {
return Sensor.SensorState.NONE.getNumber();
}
checkService();
if (service.sensorManager == null) {
Log.d(TAG, "No sensor manager for data.");
return Sensor.SensorState.NONE.getNumber();
@@ -18,7 +18,6 @@ package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.util.ApiFeatures;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -262,29 +261,22 @@ public class BluetoothConnectionManager {
}
private BluetoothSocket getSocket() throws IOException {
if (ApiFeatures.getInstance().hasBluetoothDeviceCreateInsecureRfcommSocketToServiceRecord()) {
try {
return device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
} catch (IOException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
}
} else {
try {
Class<? extends BluetoothDevice> c = device.getClass();
Method insecure = c.getMethod("createInsecureRfcommSocket", Integer.class);
insecure.setAccessible(true);
return (BluetoothSocket) insecure.invoke(device, 1);
} catch (SecurityException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (NoSuchMethodException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (IllegalAccessException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (InvocationTargetException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
}
try {
Class<? extends BluetoothDevice> c = device.getClass();
Method insecure = c.getMethod("createInsecureRfcommSocket",
Integer.class);
insecure.setAccessible(true);
return (BluetoothSocket) insecure.invoke(device, 1);
} catch (SecurityException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (NoSuchMethodException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (IllegalArgumentException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (IllegalAccessException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
} catch (InvocationTargetException e) {
Log.e(Constants.TAG, "Unable to get insecure connect.", e);
}
return device.createRfcommSocketToServiceRecord(SPP_UUID);
}
@@ -15,8 +15,6 @@
*/
package com.google.android.apps.mytracks.services.sensors;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.maps.mytracks.R;
@@ -26,13 +24,10 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
/**
* Manage the connection to a bluetooth sensor.
*
@@ -41,8 +36,8 @@ import java.util.ArrayList;
public class BluetoothSensorManager extends SensorManager {
// Local Bluetooth adapter
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
private BluetoothAdapter bluetoothAdapter = null;
// Member object for the sensor threads and connections.
private BluetoothConnectionManager connectionManager = null;
@@ -59,6 +54,8 @@ public class BluetoothSensorManager extends SensorManager {
Context context, MessageParser parser) {
this.context = context;
this.parser = parser;
// Get local Bluetooth adapter
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// If BT is not available or not enabled quit.
if (!isEnabled()) {
return;
@@ -74,50 +71,6 @@ public class BluetoothSensorManager extends SensorManager {
connectionManager = new BluetoothConnectionManager(messageHandler, parser);
}
/**
* Code for assigning the local bluetooth adapter
*
* @return The default bluetooth adapter, if one is available, NULL if it isn't.
*/
private static BluetoothAdapter getDefaultBluetoothAdapter() {
// Check if the calling thread is the main application thread,
// if it is, do it directly.
if (Thread.currentThread().equals(Looper.getMainLooper().getThread())) {
return BluetoothAdapter.getDefaultAdapter();
}
// If the calling thread, isn't the main application thread,
// then get the main application thread to return the default adapter.
final ArrayList<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(1);
final Object mutex = new Object();
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
adapters.add(BluetoothAdapter.getDefaultAdapter());
synchronized (mutex) {
mutex.notify();
}
}
});
while (adapters.isEmpty()) {
synchronized (mutex) {
try {
mutex.wait(1000L);
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while waiting for default bluetooth adapter", e);
}
}
}
if (adapters.get(0) == null) {
Log.w(TAG, "No bluetooth adapter found!");
}
return adapters.get(0);
}
public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
@@ -172,7 +125,7 @@ public class BluetoothSensorManager extends SensorManager {
}
// The Handler that gets information back from the BluetoothSensorService
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
private final Handler messageHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -1,80 +0,0 @@
/*
* Copyright 2011 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 java.util.LinkedList;
import java.util.List;
/**
* A history of Zephyr stride counter reading.
* These can be used as an alternate method to calculate the correct cadence.
* This is a work around for an issue with some HxM firmware.
*
* @author Dominik Ršttsches
*/
public class StrideReadings {
// visible for testing
protected static final int NUM_READINGS_FOR_AVERAGE = 10;
protected static final int MIN_READINGS_FOR_AVERAGE = 5;
protected static final int CADENCE_NOT_AVAILABLE = -1;
// TODO: Check whether 1Hz assumption is okay for cadence calculation
// otherwise add heart beat timestamp to this list and compute
// cadence from these timestamps.
private final List<Integer> strideReadingsHistory;
public StrideReadings() {
strideReadingsHistory = new LinkedList<Integer>();
}
public void updateStrideReading(int numStrides) {
// HRM/HxM documentation says, transmission frequency is 1 Hz,
// let's keep last NUM_READINGS_FOR_AVERAGE readings.
// TODO: Calibrate this using a reliable footpod / cadence sensor,
// otherwise use heartbeat timestamp for calculation.
strideReadingsHistory.add(0, numStrides);
while (strideReadingsHistory.size() > NUM_READINGS_FOR_AVERAGE) {
strideReadingsHistory.remove(strideReadingsHistory.size()-1);
}
}
public int getCadence() {
if (strideReadingsHistory.size() < MIN_READINGS_FOR_AVERAGE) {
// Bail out if we cannot really get a meaningful average yet.
return CADENCE_NOT_AVAILABLE;
}
// Compute assuming 1 stride reading/second.
int timeSinceOldestReadingSecs = strideReadingsHistory.size() - 1;
int stridesThen = strideReadingsHistory.get(strideReadingsHistory.size()-1);
int stridesNow = strideReadingsHistory.get(0);
// Contrary to documentation stride value seems to roll over at 128.
return Math.round( (float)(mod((stridesNow - stridesThen), 128)) /
timeSinceOldestReadingSecs * 60);
}
/**
* Modulo operation with positive return values, Java's remainder operator doesn't change sign.
*
* @return x mod y
*/
private static int mod(int x, int y)
{
int result = x % y;
return result < 0 ? result + y : result;
}
}
@@ -15,72 +15,50 @@
*/
package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.util.ApiFeatures;
import android.util.Log;
import java.util.Arrays;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
/**
* An implementation of a Sensor MessageParser for Zephyr.
*
* @author Sandor Dornbush
* @author Dominik Ršttsches
*/
public class ZephyrMessageParser implements MessageParser {
public static final int ZEPHYR_HXM_BYTE_STX = 0;
public static final int ZEPHYR_HXM_BYTE_CRC = 58;
public static final int ZEPHYR_HXM_BYTE_ETX = 59;
private static final byte[] CADENCE_BUG_FW_ID = {0x1A, 0x00, 0x31, 0x65, 0x50, 0x00, 0x31, 0x62};
private StrideReadings strideReadings;
@Override
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
Sensor.SensorDataSet.Builder sds =
Sensor.SensorDataSet.newBuilder()
.setCreationTime(System.currentTimeMillis());
Sensor.SensorData.Builder heartrate = Sensor.SensorData.newBuilder()
.setValue(buffer[12] & 0xFF)
.setState(Sensor.SensorState.SENDING);
sds.setHeartRate(heartrate);
Sensor.SensorData.Builder batteryLevel = Sensor.SensorData.newBuilder()
.setValue(buffer[11])
.setState(Sensor.SensorState.SENDING);
sds.setBatteryLevel(batteryLevel);
setCadence(sds, buffer);
return sds.build();
}
private void setCadence(Sensor.SensorDataSet.Builder sds, byte[] buffer) {
// Device Firmware ID, Firmware Version, Hardware ID, Hardware Version
// 0x1A00316550003162 produces erroneous values for Cadence and needs
// a workaround based on the stride counter.
// Firmware values range from field 3 to 10 (inclusive) of the byte buffer.
byte[] hardwareFirmwareId = ApiFeatures.getInstance().getApiPlatformAdapter()
.copyByteArray(buffer, 3, 11);
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder();
if (Arrays.equals(hardwareFirmwareId, CADENCE_BUG_FW_ID)) {
if (strideReadings == null) {
strideReadings = new StrideReadings();
}
strideReadings.updateStrideReading(buffer[54] & 0xFF);
if (strideReadings.getCadence() != StrideReadings.CADENCE_NOT_AVAILABLE) {
cadence.setValue(strideReadings.getCadence()).setState(Sensor.SensorState.SENDING);
}
} else {
cadence
.setValue(SensorUtils.unsignedShortToIntLittleEndian(buffer, 56) / 16)
.setState(Sensor.SensorState.SENDING);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < buffer.length; i++) {
sb.append(String.format("%02X", buffer[i]));
}
sds.setCadence(cadence);
Log.w(Constants.TAG, "Got zephyr data: " + sb);
// Heart Rate
Sensor.SensorData.Builder heartrate = Sensor.SensorData.newBuilder()
.setValue(buffer[12] & 0xFF)
.setState(Sensor.SensorState.SENDING);
// Changes Nico Laum (Power and Cadence)
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder()
.setValue(buffer[11])
.setState(Sensor.SensorState.SENDING);
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder()
.setValue(SensorUtils.unsignedShortToIntLittleEndian(buffer, 56) / 16)
.setState(Sensor.SensorState.SENDING);
Sensor.SensorDataSet sds =
Sensor.SensorDataSet.newBuilder()
.setCreationTime(System.currentTimeMillis())
.setPower(power)
.setHeartRate(heartrate)
.setCadence(cadence)
.build();
return sds;
}
@Override
@@ -16,11 +16,7 @@
package com.google.android.apps.mytracks.util;
import com.google.android.apps.mytracks.Constants;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.util.Log;
@@ -35,7 +31,7 @@ public class ApiFeatures {
/**
* The API level of the Android version we're being run under.
*/
private static final int ANDROID_API_LEVEL = Integer.parseInt(
public static final int ANDROID_API_LEVEL = Integer.parseInt(
Build.VERSION.SDK);
private static ApiFeatures instance;
@@ -84,17 +80,38 @@ public class ApiFeatures {
return apiPlatformAdapter;
}
// API Level 4 Changes
/**
* Returns whether cloud backup (a.k.a. Froyo backup) is available.
*/
public boolean hasBackup() {
return getApiLevel() >= 8;
}
/**
* Returns whether text-to-speech is available.
*/
public boolean hasTextToSpeech() {
return getApiLevel() >= 4;
if (getApiLevel() < 4) return false;
try {
Class.forName("android.speech.tts.TextToSpeech");
} catch (ClassNotFoundException ex) {
return false;
} catch (LinkageError er) {
return false;
}
return true;
}
public boolean hasStrictMode() {
return getApiLevel() >= 9;
}
public boolean isAudioFocusSupported() {
return getApiLevel() >= 8;
}
// API Level 5 Changes
/**
* There's a bug (#1587) in Cupcake and Donut which prevents you from
* using a SQLiteQueryBuilder twice. That is, if you call buildQuery
@@ -104,43 +121,9 @@ public class ApiFeatures {
* up. Specifically, it'll add extra parens which don't belong.
*/
public boolean canReuseSQLiteQueryBuilder() {
return getApiLevel() >= 5;
return getApiLevel() > 4;
}
// API Level 8 Changes
public boolean isAudioFocusSupported() {
return getApiLevel() >= 8;
}
/**
* Returns whether cloud backup (a.k.a. Froyo backup) is available.
*/
public boolean hasBackup() {
return getApiLevel() >= 8;
}
// API Level 9 Changes
/**
* Returns true if {@link NetHttpTransport} should be used rather than
* {@link ApacheHttpTransport}. See {@link HttpTransport} for more info.
*/
public boolean useNetHttpTransport() {
return getApiLevel() >= 9;
}
// API Level 10 changes
/**
* Returns true if
* {@link BluetoothDevice#createInsecureRfcommSocketToServiceRecord} is
* available.
*/
public boolean hasBluetoothDeviceCreateInsecureRfcommSocketToServiceRecord() {
return getApiLevel() >= 10;
}
// Visible for testing.
protected int getApiLevel() {
return ANDROID_API_LEVEL;
@@ -20,9 +20,6 @@ import android.app.NotificationManager;
import android.app.Service;
import android.content.SharedPreferences;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
* A set of methods that may be implemented in a platform specific way.
*
@@ -63,25 +60,4 @@ public interface ApiPlatformAdapter {
* Enables strict mode where supported, only if this is a development build.
*/
void enableStrictMode();
/**
* Copies elements from the input byte array into a new byte array, from
* indexes start (inclusive) to end (exclusive). The end index must be less
* than or equal to input.length.
*
* @param input the input byte array
* @param start the start index
* @param end the end index
* @return a new array containing elements from the input byte array
*/
byte[] copyByteArray(byte[] input, int start, int end);
/**
* Gets an instance of {@link DecimalFormatSymbols} for a locale.
*
* @param locale the locale
* @return a new instance of {@link DecimalFormatSymbols}
*/
DecimalFormatSymbols getDecimalFormatSymbols(Locale locale);
}
@@ -25,8 +25,6 @@ import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
* The Cupcake (API level 3) specific implementation of the
@@ -81,17 +79,4 @@ public class CupcakePlatformAdapter implements ApiPlatformAdapter {
public void enableStrictMode() {
// Not supported
}
@Override
public byte[] copyByteArray(byte[] input, int start, int end) {
int length = end - start;
byte[] output = new byte[length];
System.arraycopy(input, start, output, 0, length);
return output;
}
@Override
public DecimalFormatSymbols getDecimalFormatSymbols(Locale locale) {
return new DecimalFormatSymbols(locale);
}
}
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.util;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.SharedPreferences.Editor;
/**
* The Eclair (API level 5) specific implementation of the
@@ -25,7 +26,7 @@ import android.app.Service;
*
* @author Bartlomiej Niechwiej
*/
public class EclairPlatformAdapter extends CupcakePlatformAdapter {
public class EclairPlatformAdapter implements ApiPlatformAdapter {
@Override
public void startForeground(Service service,
@@ -39,4 +40,14 @@ public class EclairPlatformAdapter extends CupcakePlatformAdapter {
NotificationManager notificationManager, int id) {
service.stopForeground(id != -1);
}
@Override
public void applyPreferenceChanges(Editor editor) {
editor.commit();
}
@Override
public void enableStrictMode() {
// Not supported
}
}
@@ -6,10 +6,6 @@ import android.content.SharedPreferences.Editor;
import android.os.StrictMode;
import android.util.Log;
import java.text.DecimalFormatSymbols;
import java.util.Arrays;
import java.util.Locale;
/**
* The Gingerbread (API level 9) specific implememntation of the
* {@link ApiPlatformAdapter}.
@@ -17,7 +13,6 @@ import java.util.Locale;
* @author Rodrigo Damazio
*/
public class GingerbreadPlatformAdapter extends EclairPlatformAdapter {
@Override
public void applyPreferenceChanges(Editor editor) {
// Apply asynchronously
@@ -37,14 +32,4 @@ public class GingerbreadPlatformAdapter extends EclairPlatformAdapter {
.penaltyLog()
.build());
}
@Override
public byte[] copyByteArray(byte[] input, int start, int end) {
return Arrays.copyOfRange(input, start, end);
}
@Override
public DecimalFormatSymbols getDecimalFormatSymbols(Locale locale) {
return DecimalFormatSymbols.getInstance(locale);
}
}
@@ -28,8 +28,7 @@ public abstract class UnitConversions {
public static final double MI_TO_FEET = 5280.0;
public static final double KMH_TO_MPH = 1000 * M_TO_FT / MI_TO_FEET;
public static final double TO_RADIANS = Math.PI / 180.0;
public static final double MPH_TO_KMH = 1.609344;
protected UnitConversions() {
}
}
@@ -19,16 +19,6 @@ package com.google.android.apps.mytracks.widgets;
import static com.google.android.apps.mytracks.Constants.SETTINGS_NAME;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.MyTracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.services.ControlRecordingService;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@@ -42,6 +32,16 @@ import android.os.Handler;
import android.util.Log;
import android.widget.RemoteViews;
import com.google.android.apps.mytracks.MyTracks;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
/**
* An AppWidgetProvider for displaying key track statistics (distance, time,
* speed) from the current or most recent track.
@@ -175,12 +175,14 @@ public class TrackWidgetProvider
// If a new track is started by this appwidget or elsewhere,
// toggle the button to active and have it disable the track if pressed.
setButtonIntent(
views, context, R.string.end_current_track_action, R.drawable.appwidget_button_enabled);
views, context, R.string.end_current_track_action, R.drawable.appwidget_button_enabled,
-1);
} else {
// If a track is stopped by this appwidget or elsewhere,
// toggle the button to inactive and have it start a new track if pressed.
setButtonIntent(
views, context, R.string.start_new_track_action, R.drawable.appwidget_button_disabled);
views, context, R.string.start_new_track_action, R.drawable.appwidget_button_disabled,
R.string.select_new_track_extra);
}
}
@@ -191,11 +193,15 @@ public class TrackWidgetProvider
* @param context The widget context
* @param action The resource id of the action to fire when the button is pressed
* @param icon The resource id of the icon to show for the button
* @param extra Optional resource id of a boolean extra on the intent
*/
private void setButtonIntent(
RemoteViews views, Context context, int action, int icon) {
Intent intent = new Intent(context, ControlRecordingService.class);
RemoteViews views, Context context, int action, int icon, int extra) {
Intent intent = new Intent(context, TrackRecordingService.class);
intent.setAction(context.getString(action));
if (extra != -1) {
intent.putExtra(context.getString(extra), true);
}
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.appwidget_button, pendingIntent);
-1
View File
@@ -50,5 +50,4 @@ message SensorDataSet {
optional SensorData heart_rate = 2;
optional SensorData cadence = 3;
optional SensorData power = 4;
optional SensorData battery_level = 5;
}
+11
View File
@@ -23,9 +23,20 @@ limitations under the License.
<!-- Broadcast notification Intent constants -->
<string name="track_started_broadcast_action">com.google.android.apps.mytracks.TRACK_STARTED</string>
<string name="track_paused_broadcast_action">com.google.android.apps.mytracks.TRACK_PAUSED</string>
<string name="track_resumed_broadcast_action">com.google.android.apps.mytracks.TRACK_RESUMED</string>
<string name="track_stopped_broadcast_action">com.google.android.apps.mytracks.TRACK_STOPPED</string>
<string name="track_id_broadcast_extra">com.google.android.apps.mytracks.TRACK_ID</string>
<!-- TrackRecordingService actions -->
<string name="start_new_track_action">com.google.android.apps.mytracks.START_NEW_TRACK</string>
<!--
Setting this intent makes the new track the selected track.
This is not expected to work if the my tracks activity is currently active.
-->
<string name="select_new_track_extra">com.google.android.apps.mytracks.SELECT_NEW_TRACK</string>
<string name="end_current_track_action">com.google.android.apps.mytracks.END_CURRENT_TRACK</string>
<!-- My Tracks service constants -->
<string name="mytracks_service_class">com.google.android.apps.mytracks.services.TrackRecordingService</string>
<string name="mytracks_service_package">com.google.android.maps.mytracks</string>
@@ -398,18 +398,10 @@ public final class Sensor {
public boolean hasPower() { return hasPower; }
public com.google.android.apps.mytracks.content.Sensor.SensorData getPower() { return power_; }
// optional .com.google.android.apps.mytracks.content.SensorData battery_level = 5;
public static final int BATTERY_LEVEL_FIELD_NUMBER = 5;
private boolean hasBatteryLevel;
private com.google.android.apps.mytracks.content.Sensor.SensorData batteryLevel_;
public boolean hasBatteryLevel() { return hasBatteryLevel; }
public com.google.android.apps.mytracks.content.Sensor.SensorData getBatteryLevel() { return batteryLevel_; }
private void initFields() {
heartRate_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
cadence_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
power_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
batteryLevel_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
}
public final boolean isInitialized() {
if (hasHeartRate()) {
@@ -421,9 +413,6 @@ public final class Sensor {
if (hasPower()) {
if (!getPower().isInitialized()) return false;
}
if (hasBatteryLevel()) {
if (!getBatteryLevel().isInitialized()) return false;
}
return true;
}
@@ -442,9 +431,6 @@ public final class Sensor {
if (hasPower()) {
output.writeMessage(4, getPower());
}
if (hasBatteryLevel()) {
output.writeMessage(5, getBatteryLevel());
}
}
private int memoizedSerializedSize = -1;
@@ -469,10 +455,6 @@ public final class Sensor {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(4, getPower());
}
if (hasBatteryLevel()) {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(5, getBatteryLevel());
}
memoizedSerializedSize = size;
return size;
}
@@ -629,9 +611,6 @@ public final class Sensor {
if (other.hasPower()) {
mergePower(other.getPower());
}
if (other.hasBatteryLevel()) {
mergeBatteryLevel(other.getBatteryLevel());
}
return this;
}
@@ -681,15 +660,6 @@ public final class Sensor {
setPower(subBuilder.buildPartial());
break;
}
case 42: {
com.google.android.apps.mytracks.content.Sensor.SensorData.Builder subBuilder = com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder();
if (hasBatteryLevel()) {
subBuilder.mergeFrom(getBatteryLevel());
}
input.readMessage(subBuilder, extensionRegistry);
setBatteryLevel(subBuilder.buildPartial());
break;
}
}
}
}
@@ -824,43 +794,6 @@ public final class Sensor {
return this;
}
// optional .com.google.android.apps.mytracks.content.SensorData battery_level = 5;
public boolean hasBatteryLevel() {
return result.hasBatteryLevel();
}
public com.google.android.apps.mytracks.content.Sensor.SensorData getBatteryLevel() {
return result.getBatteryLevel();
}
public Builder setBatteryLevel(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
if (value == null) {
throw new NullPointerException();
}
result.hasBatteryLevel = true;
result.batteryLevel_ = value;
return this;
}
public Builder setBatteryLevel(com.google.android.apps.mytracks.content.Sensor.SensorData.Builder builderForValue) {
result.hasBatteryLevel = true;
result.batteryLevel_ = builderForValue.build();
return this;
}
public Builder mergeBatteryLevel(com.google.android.apps.mytracks.content.Sensor.SensorData value) {
if (result.hasBatteryLevel() &&
result.batteryLevel_ != com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance()) {
result.batteryLevel_ =
com.google.android.apps.mytracks.content.Sensor.SensorData.newBuilder(result.batteryLevel_).mergeFrom(value).buildPartial();
} else {
result.batteryLevel_ = value;
}
result.hasBatteryLevel = true;
return this;
}
public Builder clearBatteryLevel() {
result.hasBatteryLevel = false;
result.batteryLevel_ = com.google.android.apps.mytracks.content.Sensor.SensorData.getDefaultInstance();
return this;
}
// @@protoc_insertion_point(builder_scope:com.google.android.apps.mytracks.content.SensorDataSet)
}
@@ -16,46 +16,115 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.maps.SingleColorTrackPathPainter;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Projection;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.location.Location;
import android.test.AndroidTestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* Tests for the MyTracks map overlay.
*
* @author Bartlomiej Niechwiej
* @author Vangelis S.
*/
public class MapOverlayTest extends AndroidTestCase {
private Canvas canvas;
private MockMyTracksOverlay myTracksOverlay;
private MapView mockView;
private Projection mockProjection;
/**
* A mock version of {@code MapOverlay} that does not use
* {@class MapView}.
*/
private class MockMyTracksOverlay extends MapOverlay {
public MockMyTracksOverlay(Context context) {
super(context);
}
@Override
Projection getMapProjection(MapView mapView) {
return mockProjection;
}
@Override
Rect getMapViewRect(MapView mapView) {
return new Rect(0, 0, 100, 100);
}
@Override
Path newPath() {
return new MockPath();
}
}
/**
* A mock class that intercepts {@code Path}'s and records calls to
* {@code #moveTo()} and {@code #lineTo()}.
*/
private static class MockPath extends Path {
/** A list of disjoined path segments. */
public final List<List<PointF>> segments = new LinkedList<List<PointF>>();
/** The total number of points in this path. */
public int totalPoints;
private List<PointF> currentSegment;
@Override
public void lineTo(float x, float y) {
super.lineTo(x, y);
assertNotNull(currentSegment);
currentSegment.add(new PointF(x, y));
totalPoints++;
}
@Override
public void moveTo(float x, float y) {
super.moveTo(x, y);
segments.add(currentSegment =
new ArrayList<PointF>(Arrays.asList(new PointF(x, y))));
totalPoints++;
}
}
/**
* A mock {@code Projection} that acts as the identity matrix.
*/
private static class MockProjection implements Projection {
@Override
public Point toPixels(GeoPoint in, Point out) {
return out;
}
@Override
public float metersToEquatorPixels(float meters) {
return meters;
}
@Override
public GeoPoint fromPixels(int x, int y) {
return new GeoPoint(y, x);
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
canvas = new Canvas();
myTracksOverlay = new MockMyTracksOverlay(getContext());
// Enable drawing.
myTracksOverlay.setTrackDrawingEnabled(true);
// Set a TrackPathPainter with a MockPath.
myTracksOverlay.setTrackPathPainter(new SingleColorTrackPathPainter(getContext()) {
@Override
public Path newPath() {
return new MockPath();
}
});
mockView = null;
mockProjection = new MockProjection();
}
public void testAddLocation() throws Exception {
public void testAddLocation() throws Exception {
Location location = new Location("gps");
location.setLatitude(10);
location.setLongitude(20);
@@ -68,21 +137,19 @@ public class MapOverlayTest extends AndroidTestCase {
myTracksOverlay.addLocation(location);
assertEquals(2, myTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertNull(myTracksOverlay.getLastPath());
// Draw and make sure that we don't lose any point.
myTracksOverlay.draw(canvas, mockView, false);
assertEquals(2, myTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath);
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath();
assertEquals(2, path.totalPoints);
assertNotNull(myTracksOverlay.getLastPath());
assertEquals(2, ((MockPath)myTracksOverlay.getLastPath()).totalPoints);
myTracksOverlay.draw(canvas, mockView, true);
assertEquals(2, myTracksOverlay.getNumLocations());
assertEquals(0, myTracksOverlay.getNumWaypoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertNotNull(myTracksOverlay.getLastPath());
}
public void testClearPoints() throws Exception {
@@ -115,7 +182,7 @@ public class MapOverlayTest extends AndroidTestCase {
myTracksOverlay.addWaypoint(waypoint);
assertEquals(1, myTracksOverlay.getNumWaypoints());
assertEquals(0, myTracksOverlay.getNumLocations());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertNull(myTracksOverlay.getLastPath());
final int waypoints = 10;
for (int i = 0; i < waypoints; ++i) {
@@ -125,7 +192,7 @@ public class MapOverlayTest extends AndroidTestCase {
}
assertEquals(1 + waypoints, myTracksOverlay.getNumWaypoints());
assertEquals(0, myTracksOverlay.getNumLocations());
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertNull(myTracksOverlay.getLastPath());
}
public void testClearWaypoints() throws Exception {
@@ -159,15 +226,15 @@ public class MapOverlayTest extends AndroidTestCase {
// Shadow.
myTracksOverlay.draw(canvas, mockView, true);
// We don't expect to do anything if
assertNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertNull(myTracksOverlay.getLastPath());
assertEquals(40, myTracksOverlay.getNumWaypoints());
assertEquals(100, myTracksOverlay.getNumLocations());
// No shadow.
myTracksOverlay.draw(canvas, mockView, false);
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
assertTrue(myTracksOverlay.getTrackPathPainter().getLastPath() instanceof MockPath);
MockPath path = (MockPath) myTracksOverlay.getTrackPathPainter().getLastPath();
assertNotNull(myTracksOverlay.getLastPath());
assertTrue(myTracksOverlay.getLastPath() instanceof MockPath);
MockPath path = (MockPath) myTracksOverlay.getLastPath();
assertEquals(40, myTracksOverlay.getNumWaypoints());
assertEquals(100, myTracksOverlay.getNumLocations());
assertEquals(100, path.totalPoints);
@@ -1,51 +0,0 @@
/*
* Copyright 2011 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.MapView;
import com.google.android.maps.Projection;
import android.content.Context;
import android.graphics.Rect;
/**
* Elements for Tests for the MyTracks map overlay.
*
* @author Bartlomiej Niechwiej
* @author Vangelis S.
*
* A mock version of {@code MapOverlay} that does not use
* {@class MapView}.
*/
public class MockMyTracksOverlay extends MapOverlay {
private Projection mockProjection;
public MockMyTracksOverlay(Context context) {
super(context);
mockProjection = new MockProjection();
}
@Override
public Projection getMapProjection(MapView mapView) {
return mockProjection;
}
@Override
public Rect getMapViewRect(MapView mapView) {
return new Rect(0, 0, 100, 100);
}
}
@@ -1,60 +0,0 @@
/*
* Copyright 2011 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 android.graphics.Path;
import android.graphics.PointF;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import junit.framework.Assert;
/**
* Elements for Tests for the MyTracks map overlay.
*
* @author Bartlomiej Niechwiej
* @author Vangelis S.
*
* A mock class that intercepts {@code Path}'s and records calls to
* {@code #moveTo()} and {@code #lineTo()}.
*/
public class MockPath extends Path {
/** A list of disjoined path segments. */
public final List<List<PointF>> segments = new LinkedList<List<PointF>>();
/** The total number of points in this path. */
public int totalPoints;
private List<PointF> currentSegment;
@Override
public void lineTo(float x, float y) {
super.lineTo(x, y);
Assert.assertNotNull(currentSegment);
currentSegment.add(new PointF(x, y));
totalPoints++;
}
@Override
public void moveTo(float x, float y) {
super.moveTo(x, y);
segments.add(currentSegment =
new ArrayList<PointF>(Arrays.asList(new PointF(x, y))));
totalPoints++;
}
}
@@ -1,47 +0,0 @@
/*
* Copyright 2011 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.GeoPoint;
import com.google.android.maps.Projection;
import android.graphics.Point;
/**
* Elements for Tests for the MyTracks map overlay.
*
* @author Bartlomiej Niechwiej
* @author Vangelis S.
*
* A mock {@code Projection} that acts as the identity matrix.
*/
public class MockProjection implements Projection {
@Override
public Point toPixels(GeoPoint in, Point out) {
return out;
}
@Override
public float metersToEquatorPixels(float meters) {
return meters;
}
@Override
public GeoPoint fromPixels(int x, int y) {
return new GeoPoint(y, x);
}
}
@@ -49,6 +49,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
protected void tearDown() throws Exception {
clearSelectedAndRecordingTracks();
waitForIdle();
super.tearDown();
}
@@ -84,7 +85,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Simulate start with ACTION_VIEW intent.
Intent startIntent = new Intent();
startIntent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(File.createTempFile("valid", ".gpx", getActivity().getFilesDir()));
Uri uri = Uri.fromFile(File.createTempFile("valid", ".gpx"));
// TODO: Add a valid GPX.
@@ -107,7 +108,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Simulate start with ACTION_VIEW intent.
Intent startIntent = new Intent();
startIntent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(File.createTempFile("invalid", ".gpx", getActivity().getFilesDir()));
Uri uri = Uri.fromFile(File.createTempFile("invalid", ".gpx"));
startIntent.setData(uri);
setActivityIntent(startIntent);
@@ -167,13 +168,8 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// properly saved.
// Simulate a click on Save button.
final Button save = (Button) activity.findViewById(R.id.trackdetails_save);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
save.performClick();
}
});
Button save = (Button) activity.findViewById(R.id.trackdetails_save);
save.performClick();
// Check the remaining properties.
recordingTrackId = awaitRecordingStatus(5000, false);
@@ -28,8 +28,6 @@ import android.test.mock.MockContext;
import android.test.mock.MockResources;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;
import junit.framework.TestCase;
@@ -57,13 +55,13 @@ public class DocsHelper_AddTrackRowTest extends TestCase {
StringWritingDocsHelper docsHelper = new StringWritingDocsHelper();
addTrackRow(docsHelper, false);
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
String expectedData =
"<entry xmlns='http://www.w3.org/2005/Atom' "
+ "xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'>"
+ "<gsx:name><![CDATA[trackName]]></gsx:name>"
+ "<gsx:description><![CDATA[trackDescription]]></gsx:description>"
+ "<gsx:date><![CDATA[" + dateFormat.format(new Date(TIME)) + "]]></gsx:date>"
// We format the date here because we can't guarantee/force the timezone
+ "<gsx:date><![CDATA[" + String.format("%tc", TIME) + "]]></gsx:date>"
+ "<gsx:totaltime><![CDATA[0:00:05]]></gsx:totaltime>"
+ "<gsx:movingtime><![CDATA[0:00:04]]></gsx:movingtime>"
+ "<gsx:distance><![CDATA[12.43]]></gsx:distance>"
@@ -78,12 +76,12 @@ public class DocsHelper_AddTrackRowTest extends TestCase {
+ "<gsx:maxelevation><![CDATA[1,804]]></gsx:maxelevation>"
+ "<gsx:elevationunit><![CDATA[feet]]></gsx:elevationunit>"
+ "<gsx:map>"
+ "<![CDATA[https://maps.google.com/maps/ms?msa=0&msid=trackMapId]]>"
+ "<![CDATA[http://maps.google.com/maps/ms?msa=0&msid=trackMapId]]>"
+ "</gsx:map>"
+ "</entry>";
assertEquals(
"https://spreadsheets.google.com/feeds/list/ssid/wsid/private/full",
"http://spreadsheets.google.com/feeds/list/ssid/wsid/private/full",
docsHelper.writtenSheetUri);
assertEquals(expectedData, docsHelper.writtenData);
}
@@ -53,7 +53,7 @@ public class WriteProgressControllerTest extends ActivityInstrumentationTestCase
}
};
final WriteProgressController controller = new WriteProgressController(
WriteProgressController controller = new WriteProgressController(
getActivity(), mockWriter, SaveActivity.PROGRESS_DIALOG);
controller.setOnCompletionListener(new WriteProgressController.OnCompletionListener() {
@Override
@@ -62,22 +62,6 @@ public class WriteProgressControllerTest extends ActivityInstrumentationTestCase
}
});
/*
* The WriteProgressController constructor calls the mockWriter's
* setOnCompletionListener method with a listener that dismisses the
* progress dialog. However, this unit test only tests the
* WriteProgressController and doesn't actually show any progress dialog.
* Thus after the WriteProgressController is setup, we need to call the
* mockWriter's setOnCompletionListener method again with an listener that
* doesn't dismiss dialog.
*/
mockWriter.setOnCompletionListener(new TrackWriter.OnCompletionListener() {
@Override
public void onComplete() {
controller.getOnCompletionListener().onComplete();
}
});
dialogRef.set(controller.createProgressDialog());
controller.startWrite();
@@ -85,6 +69,7 @@ public class WriteProgressControllerTest extends ActivityInstrumentationTestCase
// wait for the writer to finish
writerDone.acquire();
assertFalse(dialogRef.get().isShowing());
assertTrue(controllerDoneRef.get());
}
}
@@ -1,52 +0,0 @@
/*
* Copyright 2011 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.maps;
import android.location.Location;
/**
* Tests for the MyTracks track path descriptors and painters.
*
* @author Vangelis S.
*/
public class TrackPathDescriptorDynamicSpeedTest extends TrackPathPainterTestCase {
public void testDynamicSpeedTrackPathDescriptor() throws Exception {
Location location = new Location("gps");
location.setLatitude(10);
for (int i = 0; i < 100; ++i) {
location = new Location("gps");
location.setLatitude(20 + i / 2);
location.setLongitude(150 - i);
myTracksOverlay.addLocation(location);
}
TrackPathPainter painter = new DynamicSpeedTrackPathPainter(
getContext(), new DynamicSpeedTrackPathDescriptor(getContext()));
myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
painter.drawTrack(canvas);
}
}
@@ -1,52 +0,0 @@
/*
* Copyright 2011 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.maps;
import android.location.Location;
/**
* Tests for the MyTracks track path descriptors and painters.
*
* @author Vangelis S.
*/
public class TrackPathDescriptorFixedSpeedTest extends TrackPathPainterTestCase {
public void testFixedSpeedTrackPathDescriptor() throws Exception {
Location location = new Location("gps");
location.setLatitude(10);
for (int i = 0; i < 100; ++i) {
location = new Location("gps");
location.setLatitude(20 + i / 2);
location.setLongitude(150 - i);
myTracksOverlay.addLocation(location);
}
TrackPathPainter painter = new DynamicSpeedTrackPathPainter(
getContext(), new FixedSpeedTrackPathDescriptor(getContext()));
myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
painter.drawTrack(canvas);
}
}
@@ -1,77 +0,0 @@
/*
* Copyright 2011 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.maps;
import com.google.android.apps.mytracks.Constants;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.content.SharedPreferences;
import android.location.Location;
/**
* Tests for the MyTracks track path painter factory.
*
* @author Vangelis S.
*/
public class TrackPathPainterFactoryTest extends TrackPathPainterTestCase {
public void testTrackPathPainterFactory() throws Exception {
Location location = new Location("gps");
location.setLatitude(10);
for (int i = 0; i < 100; ++i) {
location = new Location("gps");
location.setLatitude(20 + i / 2);
location.setLongitude(150 - i);
myTracksOverlay.addLocation(location);
}
Context context = getContext();
SharedPreferences prefs = context.getSharedPreferences(Constants.SETTINGS_NAME, 0);
if (prefs == null) {
return;
}
testTrackPathPainterFactorySpecific(context, prefs, R.string.track_color_mode_none,
SingleColorTrackPathPainter.class);
testTrackPathPainterFactorySpecific(context, prefs, R.string.track_color_mode_fixed,
DynamicSpeedTrackPathPainter.class);
testTrackPathPainterFactorySpecific(context, prefs, R.string.track_color_mode_dynamic,
DynamicSpeedTrackPathPainter.class);
}
private <T> void testTrackPathPainterFactorySpecific(Context context, SharedPreferences prefs,
int track_color_mode, Class <?> c) {
prefs.edit().putString(context.getString(R.string.track_color_mode_key),
context.getString(track_color_mode)).commit();
int startLocationIdx = 0;
Boolean alwaysVisible = true;
TrackPathPainter painter = TrackPathPainterFactory.getTrackPathPainter(context);
myTracksOverlay.setTrackPathPainter(painter);
assertNotNull(painter);
assertTrue(c.isInstance(painter));
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
painter.drawTrack(canvas);
}
}
@@ -1,51 +0,0 @@
/*
* Copyright 2011 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.maps;
import android.location.Location;
/**
* Tests for the MyTracks track path descriptors and painters.
*
* @author Vangelis S.
*/
public class TrackPathPainterSingleColorTest extends TrackPathPainterTestCase {
public void testSimpeColorTrackPathPainter() throws Exception {
Location location = new Location("gps");
location.setLatitude(10);
for (int i = 0; i < 100; ++i) {
location = new Location("gps");
location.setLatitude(20 + i / 2);
location.setLongitude(150 - i);
myTracksOverlay.addLocation(location);
}
TrackPathPainter painter = new SingleColorTrackPathPainter(getContext());
myTracksOverlay.setTrackPathPainter(painter);
int startLocationIdx = 0;
Boolean alwaysVisible = true;
assertNotNull(painter);
painter.updatePath(myTracksOverlay.getMapProjection(mockView),
myTracksOverlay.getMapViewRect(mockView), startLocationIdx, alwaysVisible,
myTracksOverlay.getPoints());
assertNotNull(myTracksOverlay.getTrackPathPainter().getLastPath());
painter.drawTrack(canvas);
}
}
@@ -1,43 +0,0 @@
/*
* Copyright 2011 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.maps;
import com.google.android.apps.mytracks.MockMyTracksOverlay;
import com.google.android.maps.MapView;
import android.graphics.Canvas;
import android.test.AndroidTestCase;
/**
* Tests for the MyTracks track path descriptors and painters.
*
* @author Vangelis S.
*/
public class TrackPathPainterTestCase extends AndroidTestCase {
protected Canvas canvas;
protected MockMyTracksOverlay myTracksOverlay;
protected MapView mockView;
@Override
protected void setUp() throws Exception {
super.setUp();
canvas = new Canvas();
myTracksOverlay = new MockMyTracksOverlay(getContext());
// Enable drawing.
myTracksOverlay.setTrackDrawingEnabled(true);
mockView = null;
}
}
@@ -405,10 +405,12 @@ public class TrackRecordingServiceTest
ITrackRecordingService service = bindAndGetService(createStartIntent());
assertTrue(service.isRecording());
// Starting a new track when there is a recording should just return -1L.
long newTrack = service.startNewTrack();
assertEquals(-1L, newTrack);
try {
service.startNewTrack();
fail("Expecting IllegalStateException");
} catch (IllegalStateException e) {
// Expected.
}
assertEquals(123, sharedPreferences.getLong(
context.getString(R.string.recording_track_key), 0));
assertEquals(123, service.getRecordingTrackId());
@@ -450,9 +452,13 @@ public class TrackRecordingServiceTest
ITrackRecordingService service = bindAndGetService(createStartIntent());
assertFalse(service.isRecording());
// Ending the current track when there is no recording should not result in any error.
service.endCurrentTrack();
// End the current track.
try {
service.endCurrentTrack();
fail("Expecting IllegalStateException");
} catch (IllegalStateException e) {
// Expected.
}
assertEquals(-1, sharedPreferences.getLong(
context.getString(R.string.recording_track_key), 0));
assertEquals(-1, service.getRecordingTrackId());
@@ -1,63 +0,0 @@
/*
* Copyright 2011 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 junit.framework.TestCase;
/**
* @author Dominik Ršttsches
*/
public class StrideReadingsTest extends TestCase {
public void testNoReadingOnStartup() {
StrideReadings strideReadings = new StrideReadings();
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
}
public void testAverageCadenceAvailable() {
StrideReadings strideReadings = new StrideReadings();
// 2 steps / second => Cadence is 120 / minute
for (int i = 1; i <= 30; i++) {
strideReadings.updateStrideReading(i*2);
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
assertEquals(120, strideReadings.getCadence());
} else {
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
}
}
}
/**
* Tests for correct calculation after rolling over at 128 strides,
* just like the HxM seems to do it.
*/
public void testRollover() {
StrideReadings strideReadings = new StrideReadings();
// 1 step per second => Cadence is 60 / minute
// Updating readings counting upwards from initialStrides -
// initialStrides set to a value below 128 to ensure rollover.
int initialStrides = 128 - StrideReadings.NUM_READINGS_FOR_AVERAGE - 5;
for (int i = 1; i <= StrideReadings.NUM_READINGS_FOR_AVERAGE + 10; i++) {
strideReadings.updateStrideReading((initialStrides + i) % 128);
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
assertEquals(60, strideReadings.getCadence());
} else {
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
}
}
}
}
@@ -26,9 +26,9 @@ public class ZephyrMessageParserTest extends TestCase {
public void testParseBuffer() {
byte[] buf = new byte[60];
// Heart Rate (-1 =^ 255 unsigned byte)
// Heartrate (-1 =^ 255 unsigned byte)
buf[12] = -1;
// Battery Level
// Power
buf[11] = 51;
// Cadence (=^ 255*16 strides/min)
buf[56] = -1;
@@ -37,9 +37,9 @@ public class ZephyrMessageParserTest extends TestCase {
assertTrue(sds.hasHeartRate());
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
assertEquals(255, sds.getHeartRate().getValue());
assertTrue(sds.hasBatteryLevel());
assertTrue(sds.getBatteryLevel().getState() == Sensor.SensorState.SENDING);
assertEquals(51, sds.getBatteryLevel().getValue());
assertTrue(sds.hasPower());
assertTrue(sds.getPower().getState() == Sensor.SensorState.SENDING);
assertEquals(51, sds.getPower().getValue());
assertTrue(sds.hasCadence());
assertTrue(sds.getCadence().getState() == Sensor.SensorState.SENDING);
assertEquals(255, sds.getCadence().getValue());

Some files were not shown because too many files have changed in this diff Show More