diff --git a/MyTracks/.classpath b/MyTracks/.classpath
index 82b4be14d..0ca188f97 100644
--- a/MyTracks/.classpath
+++ b/MyTracks/.classpath
@@ -1,22 +1,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml
index d64f76a7c..6b255feb1 100755
--- a/MyTracks/AndroidManifest.xml
+++ b/MyTracks/AndroidManifest.xml
@@ -190,28 +190,6 @@ limitations under the License.
android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -244,9 +222,10 @@ limitations under the License.
android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" />
+
+ android:label="@string/track_edit_title"
+ android:name="com.google.android.apps.mytracks.TrackEditActivity" >
@@ -257,6 +236,11 @@ limitations under the License.
+
+
+
+
+
diff --git a/MyTracks/libs/README.txt b/MyTracks/libs/README.txt
index 0df5b07bb..20bcb1ded 100644
--- a/MyTracks/libs/README.txt
+++ b/MyTracks/libs/README.txt
@@ -3,10 +3,21 @@ 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.
+Android Support Package:
+android-support-v4.jar
+- see http://developer.android.com/sdk/compatibility-library.html
+
+Google APIs Client Library for Java:
+commons-codec-1.3.jar
+commons-logging-1.1.1.jar
+google-api-client-1.5.0-beta.jar
+google-http-client-1.5.0-beta.jar
+google-oauth-client-1.5.0-beta.jar
+gson-1.6.jar
+guava-r09.jar
+jackson-core-asl-1.6.7.jar
+jsr305-1.3.9.jar
+- see http://code.google.com/p/google-api-java-client/
- 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.
@@ -14,9 +25,9 @@ google-api-client/
some of its classes are already included in the android framework, thus not
including xpp3-1.1.4c.jar.
-google-common.jar
-
-googleloginclient-helper.jar
-
+Google Analytics:
libGoogleAnalytics.jar
-- see http://code.google.com/mobile/analytics/docs/.
+- see http://code.google.com/apis/analytics/docs/mobile/android.html
+
+Others:
+google-common.jar
\ No newline at end of file
diff --git a/MyTracks/libs/google-api-client/commons-codec-1.3.jar b/MyTracks/libs/commons-codec-1.3.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/commons-codec-1.3.jar
rename to MyTracks/libs/commons-codec-1.3.jar
diff --git a/MyTracks/libs/google-api-client/commons-logging-1.1.1.jar b/MyTracks/libs/commons-logging-1.1.1.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/commons-logging-1.1.1.jar
rename to MyTracks/libs/commons-logging-1.1.1.jar
diff --git a/MyTracks/libs/google-api-client/google-api-client-1.5.0-beta.jar b/MyTracks/libs/google-api-client-1.5.0-beta.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/google-api-client-1.5.0-beta.jar
rename to MyTracks/libs/google-api-client-1.5.0-beta.jar
diff --git a/MyTracks/libs/google-api-client/google-http-client-1.5.0-beta.jar b/MyTracks/libs/google-http-client-1.5.0-beta.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/google-http-client-1.5.0-beta.jar
rename to MyTracks/libs/google-http-client-1.5.0-beta.jar
diff --git a/MyTracks/libs/google-api-client/google-oauth-client-1.5.0-beta.jar b/MyTracks/libs/google-oauth-client-1.5.0-beta.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/google-oauth-client-1.5.0-beta.jar
rename to MyTracks/libs/google-oauth-client-1.5.0-beta.jar
diff --git a/MyTracks/libs/googleloginclient-helper.jar b/MyTracks/libs/googleloginclient-helper.jar
deleted file mode 100644
index 6e610e991..000000000
Binary files a/MyTracks/libs/googleloginclient-helper.jar and /dev/null differ
diff --git a/MyTracks/libs/google-api-client/gson-1.6.jar b/MyTracks/libs/gson-1.6.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/gson-1.6.jar
rename to MyTracks/libs/gson-1.6.jar
diff --git a/MyTracks/libs/google-api-client/guava-r09.jar b/MyTracks/libs/guava-r09.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/guava-r09.jar
rename to MyTracks/libs/guava-r09.jar
diff --git a/MyTracks/libs/google-api-client/jackson-core-asl-1.6.7.jar b/MyTracks/libs/jackson-core-asl-1.6.7.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/jackson-core-asl-1.6.7.jar
rename to MyTracks/libs/jackson-core-asl-1.6.7.jar
diff --git a/MyTracks/libs/google-api-client/jsr305-1.3.9.jar b/MyTracks/libs/jsr305-1.3.9.jar
similarity index 100%
rename from MyTracks/libs/google-api-client/jsr305-1.3.9.jar
rename to MyTracks/libs/jsr305-1.3.9.jar
diff --git a/MyTracks/proguard.cfg b/MyTracks/proguard.cfg
index 0be4447b2..c9c6588f2 100644
--- a/MyTracks/proguard.cfg
+++ b/MyTracks/proguard.cfg
@@ -1,44 +1,43 @@
--target 6
--optimizationpasses 5
+#
+# Section 1
+# Default eclipse project proguard settings.
+#
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
--dontpreverify
-verbose
--optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-# Add the libraries, but ignore warnings about them
--libraryjars libs
--libraryjars ../MyTracksLib/libs
--dontwarn com.google.android.googleapps.**
--dontwarn com.android.common.**
--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.**
+-dontpreverify
-# Make our stack traces useful
-# Line numbers will be correct, file names will be replaced by "MT" since the
-# class name is enough to get the file name.
--renamesourcefileattribute MT
--keepattributes SourceFile,LineNumberTable
+-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
+# change from 5 to 2. With 5 passes, the CursorLoader in the android.support.v4 will not compile.
+# See http://stackoverflow.com/questions/6605971/android-sdk-tools-revision-12-has-problem-with-proguard-error-conversion-to
+-optimizationpasses 2
+-allowaccessmodification
-# Keep everything android references from XMLs
+-keepattributes *Annotation*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
--keep public class * extends android.app.backup.BackupAgent
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgent
-keep public class * extends android.preference.Preference
+-keep public class * extends android.support.v4.app.Fragment
+-keep public class * extends android.app.Fragment
-keep public class com.android.vending.licensing.ILicensingService
--keepclasseswithmembers class * {
+# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
+-keepclasseswithmembernames class * {
native ;
}
+-keep public class * extends android.view.View {
+ public (android.content.Context);
+ public (android.content.Context, android.util.AttributeSet);
+ public (android.content.Context, android.util.AttributeSet, int);
+ public void set*(...);
+}
+
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
}
@@ -47,6 +46,11 @@
public (android.content.Context, android.util.AttributeSet, int);
}
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
@@ -56,20 +60,55 @@
public static final android.os.Parcelable$Creator *;
}
-# Needed by google-api-client to keep generic types and @Key annotations accessed via reflection
+-keepclassmembers class **.R$* {
+ public static ;
+}
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version. We know about them, and they are safe.
+-dontwarn android.support.**
+
+#
+# Section 2
+# Google APIs Client Library for Java proguard settings.
+#
+
+# Needed by google-api-client to keep generic types and @Key annotations accessed via reflection
-keepclassmembers class * {
@com.google.api.client.util.Key ;
}
-
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
# Needed by Guava
-
-dontwarn sun.misc.Unsafe
+# See https://groups.google.com/forum/#!topic/guava-discuss/YCZzeCiIVoI
+-dontwarn com.google.common.collect.MinMaxPriorityQueue
+
+#
+# Section 3
+# My Tracks specific proguard settings.
+#
+
+# Add the libraries, but ignore warnings about them
+-libraryjars libs
+-libraryjars ../MyTracksLib/libs
+-dontwarn com.android.common.**
+-dontwarn com.google.android.googleapps.**
+-dontwarn com.google.android.gsf.**
+-dontwarn javax.servlet.**
+-dontwarn org.apache.avalon.framework.logger.**
+-dontwarn org.apache.log.**
+-dontwarn org.apache.log4j.**
+
+# Make our stack traces useful
+# Line numbers will be correct, file names will be replaced by "MT" since the
+# class name is enough to get the file name.
+-renamesourcefileattribute MT
+-keepattributes SourceFile,LineNumberTable
+
# Keep services needed by ANT+ protocol
-keep public class com.dsi.ant.IAnt_6
-keep public class com.dsi.ant.IAnt
-keep public class com.dsi.ant.IServiceSettings
-
diff --git a/MyTracks/res/drawable-hdpi/ic_menu_tracklist.png b/MyTracks/res/drawable-hdpi/ic_menu_tracklist.png
deleted file mode 100644
index 68338297b..000000000
Binary files a/MyTracks/res/drawable-hdpi/ic_menu_tracklist.png and /dev/null differ
diff --git a/MyTracks/res/drawable-hdpi/menu_insert_marker.png b/MyTracks/res/drawable-hdpi/menu_insert_marker.png
new file mode 100644
index 000000000..e6c7bf317
Binary files /dev/null and b/MyTracks/res/drawable-hdpi/menu_insert_marker.png differ
diff --git a/MyTracks/res/drawable-hdpi/menu_play.png b/MyTracks/res/drawable-hdpi/menu_play.png
new file mode 100644
index 000000000..df8a2ca28
Binary files /dev/null and b/MyTracks/res/drawable-hdpi/menu_play.png differ
diff --git a/MyTracks/res/drawable-hdpi/menu_share.png b/MyTracks/res/drawable-hdpi/menu_share.png
new file mode 100644
index 000000000..c329f58da
Binary files /dev/null and b/MyTracks/res/drawable-hdpi/menu_share.png differ
diff --git a/MyTracks/res/drawable-ldpi/ic_menu_tracklist.png b/MyTracks/res/drawable-ldpi/ic_menu_tracklist.png
deleted file mode 100644
index 0ba0b6bad..000000000
Binary files a/MyTracks/res/drawable-ldpi/ic_menu_tracklist.png and /dev/null differ
diff --git a/MyTracks/res/drawable-mdpi/ic_menu_tracklist.png b/MyTracks/res/drawable-mdpi/ic_menu_tracklist.png
deleted file mode 100644
index 5c86763bb..000000000
Binary files a/MyTracks/res/drawable-mdpi/ic_menu_tracklist.png and /dev/null differ
diff --git a/MyTracks/res/drawable-mdpi/menu_insert_marker.png b/MyTracks/res/drawable-mdpi/menu_insert_marker.png
new file mode 100644
index 000000000..de9a29e7c
Binary files /dev/null and b/MyTracks/res/drawable-mdpi/menu_insert_marker.png differ
diff --git a/MyTracks/res/drawable-mdpi/menu_play.png b/MyTracks/res/drawable-mdpi/menu_play.png
new file mode 100644
index 000000000..6a40cd5f7
Binary files /dev/null and b/MyTracks/res/drawable-mdpi/menu_play.png differ
diff --git a/MyTracks/res/drawable-mdpi/menu_share.png b/MyTracks/res/drawable-mdpi/menu_share.png
new file mode 100644
index 000000000..056deb57b
Binary files /dev/null and b/MyTracks/res/drawable-mdpi/menu_share.png differ
diff --git a/MyTracks/res/drawable-xhdpi/menu_insert_marker.png b/MyTracks/res/drawable-xhdpi/menu_insert_marker.png
new file mode 100644
index 000000000..6b197c30f
Binary files /dev/null and b/MyTracks/res/drawable-xhdpi/menu_insert_marker.png differ
diff --git a/MyTracks/res/drawable-xhdpi/menu_play.png b/MyTracks/res/drawable-xhdpi/menu_play.png
new file mode 100644
index 000000000..51124993d
Binary files /dev/null and b/MyTracks/res/drawable-xhdpi/menu_play.png differ
diff --git a/MyTracks/res/drawable-xhdpi/menu_share.png b/MyTracks/res/drawable-xhdpi/menu_share.png
new file mode 100644
index 000000000..15549b04e
Binary files /dev/null and b/MyTracks/res/drawable-xhdpi/menu_share.png differ
diff --git a/MyTracks/res/layout/mytracks_layout.xml b/MyTracks/res/layout/mytracks_layout.xml
index 203d0636a..20fec4b55 100644
--- a/MyTracks/res/layout/mytracks_layout.xml
+++ b/MyTracks/res/layout/mytracks_layout.xml
@@ -59,23 +59,4 @@ limitations under the License.
android:minHeight="80dp"
android:layout_gravity="center"/>
-
-
-
-
diff --git a/MyTracks/res/layout/track_detail.xml b/MyTracks/res/layout/track_edit.xml
similarity index 84%
rename from MyTracks/res/layout/track_detail.xml
rename to MyTracks/res/layout/track_edit.xml
index fe4f4ed10..6598a5d92 100644
--- a/MyTracks/res/layout/track_detail.xml
+++ b/MyTracks/res/layout/track_edit.xml
@@ -35,7 +35,7 @@ limitations under the License.
-
+ android:icon="@drawable/menu_insert_marker"
+ android:id="@+id/menu_insert_marker"
+ android:showAsAction="always"
+ android:title="@string/menu_insert_marker"/>
+
+ -
+
+
+
+
+
+
+
+
+
+ android:id="@+id/menu_send_google"
+ android:title="@string/menu_send_google"/>
+ -
+
+
+
+
+
+
+
+
+
+ android:title="@string/menu_my_location"/>
+ android:title="@string/menu_satellite_mode"/>
+ android:icon="@drawable/ic_menu_chartsettings"
+ android:id="@+id/menu_chart_settings"
+ android:title="@string/menu_chart_settings"/>
-
-
Delete all
+
+ Delete
+
+
+ Edit
+
Export all
+
+ Export as %1$s
+
Help
@@ -989,6 +1004,10 @@ limitations under the License.
translation_description="In the menu, to import all the tracks from the SD card.">
Import all
+
+ Insert marker
+
Map mode
@@ -1001,6 +1020,10 @@ limitations under the License.
translation_description="In the menu, to display the user's current location.">
My location
+
+ Play
+
Record track
@@ -1009,10 +1032,25 @@ limitations under the License.
translation_description="In the menu, to switch the display to the satellite mode.">
Satellite mode
+
+ Save to SD card...
+
+
+ As a %1$s file
+
Search
+
+ Send to Google...
+
Sensor state
@@ -1025,11 +1063,31 @@ limitations under the License.
translation_description="In the menu, to stop recording a track.">
Stop recording
-
- Tracks
+
+ Share with friends...
+
+ A %1$s file
+
+
+ Fusion Table
+
+
+ Map
+
+
No tracks or waypoints matched your search.
+
+ In track \'%1$s\'.
+
@@ -2238,196 +2303,71 @@ limitations under the License.
-
- Activity type (e.g. hiking, cycling, running...)
+
+ The track and its markers will be permanently deleted from the phone.
-
- Activity type
-
-
- Track details
-
-
- Track description
-
-
- Track name
+
+ You need Google Earth to play the track. Do you want to install from the Play Store?
Track
-
- Clear map
+
+ Activity type (e.g. hiking, cycling, running...)
-
- This track
+
+ Activity type
+
+
+ Edit track
+
+
+ Track description
+
+
+ Track name
+ translation_description="When deleting all the tracks, the confirmation dialog message informing
+ the user that all tracks and markers will be permanently deleted from the phone.">
All tracks and markers will be permanently deleted from the phone.
-
- Delete from phone...
+
+ Unable to start recording. No GPS location. Try again.
-
- The track and its markers will be permanently deleted from the phone.
-
-
- Edit
-
-
- Export as %1$s
-
-
- Play
-
-
- You need Google Earth to play the track. Do you want to install from the Market?
-
-
- As a %1$s file
-
-
- Save to SD card...
-
-
- Send to Google...
-
-
- A %1$s file
-
-
- Fusion Table
-
-
- Map
-
-
- Share with friends...
-
-
- Show on map
-
-
- In track \'%1$s\'.
+
+ Now recording a new track.
+ %1$d: track number. E.g., '1'.">
Track %1$d
-
- Unable to start recording. No GPS location. Try again.
-
+ informing the user that My Tracks is actively recording his track.">
Recording your track...
-
- Now recording a new track.
-
= 11
- return true;
- }
-
- /**
- * Prepares the menu for display.
- *
- * @param menu the menu to update for display
- * @param hasRecorded whether any track has been recorded
- * @param isRecording whether we're recording a track now
- * @param hasSelectedTrack whether there's a track currently selected for display
- * @param isSatelliteMode whether the map is currently in satellite mode
- * @param currentTabTag the tag for the currently-displayed tab
- */
- public void onPrepareOptionsMenu(Menu menu, boolean hasRecorded,
- boolean isRecording, boolean hasSelectedTrack,
- boolean isSatelliteMode, String currentTabTag) {
- menu.findItem(R.id.menu_markers)
- .setEnabled(hasRecorded && hasSelectedTrack);
-
- recordTrack = menu.findItem(R.id.menu_record_track);
- stopRecording = menu.findItem(R.id.menu_stop_recording);
- updateActionItems(isRecording);
-
- menu.findItem(R.id.menu_chart_settings)
- .setVisible(CHART_TAB_TAG.equals(currentTabTag));
-
- boolean isMapTab = MAP_TAB_TAG.equals(currentTabTag);
- menu.findItem(R.id.menu_my_location)
- .setVisible(isMapTab);
- menu.findItem(R.id.menu_satellite_mode)
- .setVisible(isMapTab)
- .setTitle(isSatelliteMode ? R.string.menu_map_mode : R.string.menu_satellite_mode);
- }
-
- /**
- * Updates the action items.
- * TODO: also update the action items when
- *
- * start/stop a recording from the home screen widget.
- * receive a direct service call to start/stop a recording.
- *
- *
- * @param isRecording true if recording a track
- */
- private void updateActionItems(boolean isRecording) {
- recordTrack.setEnabled(!isRecording).setVisible(!isRecording);
- stopRecording.setEnabled(isRecording).setVisible(isRecording);
- }
-
- /**
- * Called when an option from the menu is selected.
- *
- * @param item the selected item
- * @return true if the action was handled, false otherwise
- */
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_record_track: {
- activity.startRecording();
- updateActionItems(true);
- return true;
- }
- case R.id.menu_stop_recording: {
- activity.stopRecording();
- updateActionItems(false);
- return true;
- }
- case R.id.menu_tracks: {
- activity.startActivityForResult(new Intent(activity, TrackListActivity.class),
- Constants.SHOW_TRACK);
- return true;
- }
- case R.id.menu_markers: {
- Intent startIntent = new Intent(activity, WaypointsList.class);
- startIntent.putExtra("trackid", activity.getSelectedTrackId());
- activity.startActivityForResult(startIntent, Constants.SHOW_WAYPOINT);
- return true;
- }
- case R.id.menu_sensor_state: {
- return startActivity(SensorStateActivity.class);
- }
- case R.id.menu_settings: {
- return startActivity(SettingsActivity.class);
- }
- case R.id.menu_aggregated_statistics: {
- return startActivity(AggregatedStatsActivity.class);
- }
- case R.id.menu_help: {
- return startActivity(WelcomeActivity.class);
- }
- case R.id.menu_search: {
- // TODO: Pass the current track ID and current location to do some fancier ranking.
- activity.onSearchRequested();
- return true;
- }
- case R.id.menu_chart_settings: {
- activity.showChartSettings();
- return true;
- }
- case R.id.menu_my_location: {
- activity.showMyLocation();
- return true;
- }
- case R.id.menu_satellite_mode: {
- activity.toggleSatelliteView();
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Starts an activity with the given class.
- */
- private boolean startActivity(Class extends Activity> activityClass) {
- activity.startActivity(new Intent(activity, activityClass));
- return true;
- }
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java
deleted file mode 100644
index 37ab619bd..000000000
--- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java
+++ /dev/null
@@ -1,580 +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 static com.google.android.apps.mytracks.Constants.CHART_TAB_TAG;
-import static com.google.android.apps.mytracks.Constants.MAP_TAB_TAG;
-import static com.google.android.apps.mytracks.Constants.STATS_TAB_TAG;
-import static com.google.android.apps.mytracks.Constants.TAG;
-
-import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
-import com.google.android.apps.mytracks.content.TrackDataHub;
-import com.google.android.apps.mytracks.content.TracksColumns;
-import com.google.android.apps.mytracks.content.Waypoint;
-import com.google.android.apps.mytracks.content.WaypointCreationRequest;
-import com.google.android.apps.mytracks.content.WaypointsColumns;
-import com.google.android.apps.mytracks.services.ITrackRecordingService;
-import com.google.android.apps.mytracks.services.ServiceUtils;
-import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
-import com.google.android.apps.mytracks.util.AnalyticsUtils;
-import com.google.android.apps.mytracks.util.ApiAdapterFactory;
-import com.google.android.apps.mytracks.util.EulaUtils;
-import com.google.android.apps.mytracks.util.UriUtils;
-import com.google.android.maps.mytracks.BuildConfig;
-import com.google.android.maps.mytracks.R;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.TabActivity;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.RelativeLayout;
-import android.widget.TabHost;
-import android.widget.Toast;
-
-/**
- * The super activity that embeds our sub activities.
- *
- * @author Leif Hendrik Wilden
- * @author Rodrigo Damazio
- */
-@SuppressWarnings("deprecation")
-public class MyTracks extends TabActivity implements OnTouchListener {
- private static final int DIALOG_EULA_ID = 0;
- private static final int DIALOG_CHECK_UNITS_ID = 1;
- private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
- private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
-
- private TrackDataHub dataHub;
-
- /**
- * Menu manager.
- */
- private MenuManager menuManager;
-
- /**
- * Preferences.
- */
- private SharedPreferences preferences;
-
- /**
- * True if a new track should be created after the track recording service
- * binds.
- */
- private boolean startNewTrackRequested = false;
-
- /**
- * Utilities to deal with the database.
- */
- private MyTracksProviderUtils providerUtils;
-
- /*
- * Tabs/View navigation:
- */
-
- private NavControls navControls;
-
- private final Runnable changeTab = new Runnable() {
- public void run() {
- getTabHost().setCurrentTab(navControls.getCurrentIcons());
- }
- };
-
- /*
- * Recording service interaction:
- */
-
- private final Runnable serviceBindCallback = new Runnable() {
- @Override
- public void run() {
- synchronized (serviceConnection) {
- ITrackRecordingService service = serviceConnection.getServiceIfBound();
- if (startNewTrackRequested && service != null) {
- Log.i(TAG, "Starting recording");
- startNewTrackRequested = false;
- startRecordingNewTrack(service);
- } else if (startNewTrackRequested) {
- Log.w(TAG, "Not yet starting recording");
- }
- }
- }
- };
-
- private TrackRecordingServiceConnection serviceConnection;
-
- /*
- * Application lifetime events:
- * ============================
- */
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Log.d(TAG, "MyTracks.onCreate");
- super.onCreate(savedInstanceState);
- if (BuildConfig.DEBUG) {
- ApiAdapterFactory.getApiAdapter().enableStrictMode();
- }
-
- AnalyticsUtils.sendPageViews(this, "/appstart");
-
- providerUtils = MyTracksProviderUtils.Factory.get(this);
- preferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
- dataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
- menuManager = new MenuManager(this);
- serviceConnection = new TrackRecordingServiceConnection(this, serviceBindCallback);
-
- setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
-
- // Show the action bar (or nothing at all).
- ApiAdapterFactory.getApiAdapter().showActionBar(this);
-
- // If the user just starts typing (on a device with a keyboard), we start a search.
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- final Resources res = getResources();
- final TabHost tabHost = getTabHost();
- tabHost.addTab(tabHost.newTabSpec(MAP_TAB_TAG)
- .setIndicator("Map", res.getDrawable(
- android.R.drawable.ic_menu_mapmode))
- .setContent(new Intent(this, MapActivity.class)));
- tabHost.addTab(tabHost.newTabSpec(STATS_TAB_TAG)
- .setIndicator("Stats", res.getDrawable(R.drawable.ic_menu_statistics))
- .setContent(new Intent(this, StatsActivity.class)));
- tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG)
- .setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
- .setContent(new Intent(this, ChartActivity.class)));
-
- // Hide the tab widget itself. We'll use overlayed prev/next buttons to
- // switch between the tabs:
- tabHost.getTabWidget().setVisibility(View.GONE);
-
- RelativeLayout layout = new RelativeLayout(this);
- LayoutParams params =
- new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
- layout.setLayoutParams(params);
- navControls =
- new NavControls(this, layout,
- getResources().obtainTypedArray(R.array.left_icons),
- getResources().obtainTypedArray(R.array.right_icons),
- changeTab);
- navControls.show();
- tabHost.addView(layout);
- layout.setOnTouchListener(this);
-
- if (!EulaUtils.getEulaValue(this)) {
- showDialog(DIALOG_EULA_ID);
- }
- }
-
- @Override
- protected void onStart() {
- Log.d(TAG, "MyTracks.onStart");
- super.onStart();
- dataHub.start();
-
- // Ensure that service is running and bound if we're supposed to be recording
- if (ServiceUtils.isRecording(this, null, preferences)) {
- serviceConnection.startAndBind();
- }
-
- Intent intent = getIntent();
- String action = intent.getAction();
- Uri data = intent.getData();
- if ((Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action))
- && TracksColumns.CONTENT_ITEMTYPE.equals(intent.getType())
- && UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
- long trackId = ContentUris.parseId(data);
- dataHub.loadTrack(trackId);
- } else if (Intent.ACTION_VIEW.equals(action)
- && WaypointsColumns.CONTENT_ITEMTYPE.equals(intent.getType())
- && UriUtils.matchesContentUri(data, WaypointsColumns.CONTENT_URI)) {
- // TODO(rdamazio): Waypoint URIs should be base/trackid/waypointid
- long waypointId = ContentUris.parseId(data);
- Waypoint waypoint = providerUtils.getWaypoint(waypointId);
- long trackId = waypoint.getTrackId();
-
- // Request that the waypoint is shown (now or when the right track is loaded).
- showWaypoint(trackId, waypointId);
-
- // Load the right track, if not loaded already.
- dataHub.loadTrack(trackId);
- }
- }
-
- @Override
- protected void onResume() {
- // Called when the current activity is being displayed or re-displayed
- // to the user.
- Log.d(TAG, "MyTracks.onResume");
- serviceConnection.bindIfRunning();
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- // Called when activity is going into the background, but has not (yet) been
- // killed. Shouldn't block longer than approx. 2 seconds.
- Log.d(TAG, "MyTracks.onPause");
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- Log.d(TAG, "MyTracks.onStop");
- dataHub.stop();
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- Log.d(TAG, "MyTracks.onDestroy");
- serviceConnection.unbind();
- super.onDestroy();
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_EULA_ID:
- return new AlertDialog.Builder(this)
- .setCancelable(true)
- .setMessage(EulaUtils.getEulaMessage(this))
- .setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- })
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- })
- .setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- EulaUtils.setEulaValue(MyTracks.this);
- startActivityForResult(
- new Intent(MyTracks.this, WelcomeActivity.class), Constants.WELCOME);
- }
- })
- .setTitle(R.string.eula_title)
- .create();
- case DIALOG_CHECK_UNITS_ID:
- return new AlertDialog.Builder(this)
- .setCancelable(true)
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- SharedPreferences sharedPreferences = getSharedPreferences(
- CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
- ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(
- sharedPreferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true));
- }
- })
- .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- SharedPreferences sharedPreferences = getSharedPreferences(
- CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
- ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(
- sharedPreferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true));
-
- int position = ((AlertDialog) dialog).getListView().getSelectedItemPosition();
- SharedPreferences.Editor editor = preferences.edit();
- ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(
- editor.putBoolean(getString(R.string.metric_units_key), position == 0));
- }
- })
- .setSingleChoiceItems(new CharSequence[] { getString(R.string.preferred_units_metric),
- getString(R.string.preferred_units_imperial) }, 0, null)
- .setTitle(R.string.preferred_units_title)
- .create();
- default:
- return null;
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- return menuManager.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MapActivity map = getMapTab();
- boolean isSatelliteView = map != null ? map.isSatelliteView() : false;
-
- menuManager.onPrepareOptionsMenu(menu, providerUtils.getLastTrack() != null,
- ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences),
- dataHub.isATrackSelected(),
- isSatelliteView,
- getTabHost().getCurrentTabTag());
-
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return menuManager.onOptionsItemSelected(item)
- ? true
- : super.onOptionsItemSelected(item);
- }
-
- /*
- * Key events:
- * ===========
- */
-
- @Override
- public boolean onTrackballEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (ServiceUtils.isRecording(this, serviceConnection.getServiceIfBound(), preferences)) {
- try {
- insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot insert statistics marker.", e);
- } catch (IllegalStateException e) {
- Log.e(TAG, "Cannot insert statistics marker.", e);
- }
- return true;
- }
- }
-
- return super.onTrackballEvent(event);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- final Intent results) {
- Log.d(TAG, "MyTracks.onActivityResult");
- long trackId = dataHub.getSelectedTrackId();
- if (results != null) {
- trackId = results.getLongExtra("trackid", trackId);
- }
-
- switch (requestCode) {
- case Constants.SHOW_TRACK: {
- if (results != null) {
- if (trackId >= 0) {
- dataHub.loadTrack(trackId);
-
- // The track list passed the requested action as result code. Hand
- // it off to the onActivityResult for further processing:
- if (resultCode != Constants.SHOW_TRACK) {
- onActivityResult(resultCode, Activity.RESULT_OK, results);
- }
- }
- }
- break;
- }
- case Constants.SHOW_WAYPOINT: {
- if (results != null) {
- final long waypointId = results.getLongExtra(WaypointDetails.WAYPOINT_ID_EXTRA, -1);
- if (waypointId >= 0) {
- MapActivity map = getMapTab();
- if (map != null) {
- getTabHost().setCurrentTab(0);
- map.showWaypoint(waypointId);
- }
- }
- }
- break;
- }
- case Constants.WELCOME: {
- SharedPreferences sharedPreferences = getSharedPreferences(
- CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
- if (!sharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false)) {
- showDialog(DIALOG_CHECK_UNITS_ID);
- }
- break;
- }
-
- default: {
- Log.w(TAG, "Warning unhandled request code: " + requestCode);
- }
- }
- }
-
- private void showWaypoint(long trackId, long waypointId) {
- MapActivity map =
- (MapActivity) getLocalActivityManager().getActivity("tab1");
- if (map != null) {
- getTabHost().setCurrentTab(0);
- map.showWaypoint(trackId, waypointId);
- } else {
- Log.e(TAG, "Couldnt' get map tab");
- }
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- navControls.show();
- }
- return false;
- }
-
- /**
- * Inserts a waypoint marker.
- *
- * TODO: Merge with WaypointsList#insertWaypoint.
- *
- * @return Id of the inserted statistics marker.
- * @throws RemoteException If the call on the service failed.
- */
- private long insertWaypoint(WaypointCreationRequest request) throws RemoteException {
- ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
- if (trackRecordingService == null) {
- throw new IllegalStateException("The recording service is not bound.");
- }
- try {
- long waypointId = trackRecordingService.insertWaypoint(request);
- if (waypointId >= 0) {
- Toast.makeText(this, R.string.marker_insert_success, Toast.LENGTH_LONG).show();
- }
- return waypointId;
- } catch (RemoteException e) {
- Toast.makeText(this, R.string.marker_insert_error, Toast.LENGTH_LONG).show();
- throw e;
- }
- }
-
- private void startRecordingNewTrack(
- ITrackRecordingService trackRecordingService) {
- try {
- long recordingTrackId = trackRecordingService.startNewTrack();
- // Select the recording track.
- dataHub.loadTrack(recordingTrackId);
- Toast.makeText(this, getString(R.string.track_record_success), Toast.LENGTH_SHORT).show();
- // TODO: We catch Exception, because after eliminating the service process
- // all exceptions it may throw are no longer wrapped in a RemoteException.
- } catch (Exception e) {
- Toast.makeText(this, getString(R.string.track_record_error), Toast.LENGTH_SHORT).show();
- Log.w(TAG, "Unable to start recording.", e);
- }
- }
-
- /**
- * Starts the track recording service (if not already running) and binds to
- * it. Starts recording a new track.
- */
- void startRecording() {
- synchronized (serviceConnection) {
- startNewTrackRequested = true;
- serviceConnection.startAndBind();
-
- // Binding was already requested before, it either already happened
- // (in which case running the callback manually triggers the actual recording start)
- // or it will happen in the future
- // (in which case running the callback now will have no effect).
- serviceBindCallback.run();
- }
- }
-
- /**
- * Stops the track recording service and unbinds from it. Will display a toast
- * "Stopped recording" and pop up the Track Details activity.
- */
- void stopRecording() {
- // Save the track id as the shared preference will overwrite the recording track id.
- SharedPreferences sharedPreferences = getSharedPreferences(
- Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
- long currentTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1);
-
- ITrackRecordingService trackRecordingService = serviceConnection.getServiceIfBound();
- if (trackRecordingService != null) {
- try {
- trackRecordingService.endCurrentTrack();
- } catch (Exception e) {
- Log.e(TAG, "Unable to stop recording.", e);
- }
- }
-
- serviceConnection.stop();
-
- if (currentTrackId > 0) {
- Intent intent = new Intent(MyTracks.this, TrackDetail.class);
- intent.putExtra(TrackDetail.TRACK_ID, currentTrackId);
- intent.putExtra(TrackDetail.SHOW_CANCEL, false);
- startActivity(intent);
- }
- }
-
- long getSelectedTrackId() {
- return dataHub.getSelectedTrackId();
- }
-
- /**
- * Asks the chart tab to show its settings.
- */
- public void showChartSettings() {
- ChartActivity chart = getChartTab();
- if (chart != null) {
- chart.showChartSettingsDialog();
- }
- }
-
- /**
- * Asks the map tab to show the map in satellite mode.
- */
- public void toggleSatelliteView() {
- MapActivity mapTab = getMapTab();
- if (mapTab != null) {
- mapTab.setSatelliteView(!mapTab.isSatelliteView());
- }
- }
-
- /**
- * Asks the map tab to jump to the current location.
- */
- public void showMyLocation() {
- MapActivity mapTab = getMapTab();
- if (mapTab != null) {
- mapTab.showMyLocation();
- }
- }
-
- /**
- * Returns the map tab instance if available, or null otherwise.
- */
- private MapActivity getMapTab() {
- return (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
- }
-
- /**
- * Returns the chart tab instance if available, or null otherwise.
- */
- private ChartActivity getChartTab() {
- return (ChartActivity) getLocalActivityManager().getActivity(CHART_TAB_TAG);
- }
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracksApplication.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracksApplication.java
index 0629f4363..d2a50f03c 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/MyTracksApplication.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracksApplication.java
@@ -17,6 +17,9 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.services.RemoveTempFilesService;
+import com.google.android.apps.mytracks.util.AnalyticsUtils;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
+import com.google.android.maps.mytracks.BuildConfig;
import android.app.Application;
import android.content.Intent;
@@ -32,6 +35,10 @@ public class MyTracksApplication extends Application {
@Override
public void onCreate() {
+ if (BuildConfig.DEBUG) {
+ ApiAdapterFactory.getApiAdapter().enableStrictMode();
+ }
+ AnalyticsUtils.sendPageViews(this, "/appstart");
startService(new Intent(this, RemoveTempFilesService.class));
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/SearchActivity.java b/MyTracks/src/com/google/android/apps/mytracks/SearchActivity.java
index 3e57d9213..3bff1746b 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/SearchActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/SearchActivity.java
@@ -23,22 +23,18 @@ import com.google.android.apps.mytracks.content.SearchEngine.ScoredResult;
import com.google.android.apps.mytracks.content.SearchEngine.SearchQuery;
import com.google.android.apps.mytracks.content.SearchEngineProvider;
import com.google.android.apps.mytracks.content.Track;
-import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.content.Waypoint;
-import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import android.app.ListActivity;
import android.app.SearchManager;
-import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationManager;
-import android.net.Uri;
import android.os.Bundle;
import android.provider.SearchRecentSuggestions;
import android.util.Log;
@@ -193,7 +189,7 @@ public class SearchActivity extends ListActivity {
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription());
resultMap.put(CATEGORY_FIELD, waypoint.getCategory());
// In the same place as we show time for tracks, show the track name for waypoints.
- resultMap.put(TIME_FIELD, getString(R.string.track_list_track_name, trackName));
+ resultMap.put(TIME_FIELD, getString(R.string.search_track_location, trackName));
resultMap.put(STATS_FIELD, StringUtils.formatDateTime(this, waypoint.getLocation().getTime()));
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
resultMap.put(WAYPOINT_ID_FIELD, waypoint.getId());
@@ -251,15 +247,12 @@ public class SearchActivity extends ListActivity {
}
private Intent createViewDataIntent(Map clickedData) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
+ Intent intent = new Intent(this, TrackDetailActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (clickedData.containsKey(WAYPOINT_ID_FIELD)) {
- long waypointId = (Long) clickedData.get(WAYPOINT_ID_FIELD);
- Uri uri = ContentUris.withAppendedId(WaypointsColumns.CONTENT_URI, waypointId);
- intent.setDataAndType(uri, WaypointsColumns.CONTENT_ITEMTYPE);
+ intent.putExtra(TrackDetailActivity.WAYPOINT_ID, (Long) clickedData.get(WAYPOINT_ID_FIELD));
} else {
- long trackId = (Long) clickedData.get(TRACK_ID_FIELD);
- Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
- intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
+ intent.putExtra(TrackDetailActivity.TRACK_ID, (Long) clickedData.get(TRACK_ID_FIELD));
}
return intent;
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java
new file mode 100644
index 000000000..ed2b52c63
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java
@@ -0,0 +1,550 @@
+/*
+ * 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 static com.google.android.apps.mytracks.Constants.CHART_TAB_TAG;
+import static com.google.android.apps.mytracks.Constants.MAP_TAB_TAG;
+import static com.google.android.apps.mytracks.Constants.STATS_TAB_TAG;
+
+import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
+import com.google.android.apps.mytracks.content.TrackDataHub;
+import com.google.android.apps.mytracks.content.Waypoint;
+import com.google.android.apps.mytracks.content.WaypointCreationRequest;
+import com.google.android.apps.mytracks.io.file.SaveActivity;
+import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
+import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
+import com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity;
+import com.google.android.apps.mytracks.services.ITrackRecordingService;
+import com.google.android.apps.mytracks.services.ServiceUtils;
+import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
+import com.google.android.apps.mytracks.util.DialogUtils;
+import com.google.android.apps.mytracks.util.PlayTrackUtils;
+import com.google.android.maps.mytracks.R;
+
+import android.app.Dialog;
+import android.app.TabActivity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.speech.tts.TextToSpeech;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.RelativeLayout;
+import android.widget.TabHost;
+import android.widget.Toast;
+
+/**
+ * An activity to show the track detail.
+ *
+ * @author Leif Hendrik Wilden
+ * @author Rodrigo Damazio
+ */
+@SuppressWarnings("deprecation")
+public class TrackDetailActivity extends TabActivity implements OnTouchListener {
+
+ public static final String TRACK_ID = "track_id";
+ public static final String WAYPOINT_ID = "waypoint_id";
+
+ private static final String TAG = TrackDetailActivity.class.getSimpleName();
+ private static final int DIALOG_INSTALL_EARTH_ID = 0;
+ private static final int DIALOG_DELETE_CURRENT_ID = 1;
+
+ private SharedPreferences sharedPreferences;
+ private TrackDataHub trackDataHub;
+ private TrackRecordingServiceConnection trackRecordingServiceConnection;
+ private NavControls navControls;
+
+ private MenuItem stopRecordingMenuItem;
+ private MenuItem insertMarkerMenuItem;
+ private MenuItem playMenuItem;
+ private MenuItem shareMenuItem;
+ private MenuItem sendGoogleMenuItem;
+ private MenuItem saveMenuItem;
+ private MenuItem editMenuItem;
+ private MenuItem deleteMenuItem;
+
+ private final Runnable changeTab = new Runnable() {
+ public void run() {
+ getTabHost().setCurrentTab(navControls.getCurrentIcons());
+ }
+ };
+
+ /*
+ * Note that sharedPreferenceChangeListener cannot be an anonymous inner
+ * class. Anonymous inner class will get garbage collected.
+ */
+ private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener =
+ new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ // Note that key can be null
+ if (getString(R.string.recording_track_key).equals(key)) {
+ if (isRecording()) {
+ trackRecordingServiceConnection.startAndBind();
+ }
+ updateMenu();
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
+ sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
+ trackDataHub = ((MyTracksApplication) getApplication()).getTrackDataHub();
+ trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
+
+ setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
+
+ // Show the action bar (or nothing at all).
+ ApiAdapterFactory.getApiAdapter().showActionBar(this);
+
+ final Resources res = getResources();
+ final TabHost tabHost = getTabHost();
+ tabHost.addTab(tabHost.newTabSpec(MAP_TAB_TAG)
+ .setIndicator("Map", res.getDrawable(android.R.drawable.ic_menu_mapmode))
+ .setContent(new Intent(this, MapActivity.class)));
+ tabHost.addTab(tabHost.newTabSpec(STATS_TAB_TAG)
+ .setIndicator("Stats", res.getDrawable(R.drawable.ic_menu_statistics))
+ .setContent(new Intent(this, StatsActivity.class)));
+ tabHost.addTab(tabHost.newTabSpec(CHART_TAB_TAG)
+ .setIndicator("Chart", res.getDrawable(R.drawable.menu_elevation))
+ .setContent(new Intent(this, ChartActivity.class)));
+
+ // Hide the tab widget itself. We'll use overlayed prev/next buttons to
+ // switch between the tabs:
+ tabHost.getTabWidget().setVisibility(View.GONE);
+
+ RelativeLayout layout = new RelativeLayout(this);
+ LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+ layout.setLayoutParams(params);
+ navControls = new NavControls(this, layout, getResources().obtainTypedArray(R.array.left_icons),
+ getResources().obtainTypedArray(R.array.right_icons), changeTab);
+ navControls.show();
+ tabHost.addView(layout);
+ layout.setOnTouchListener(this);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ trackDataHub.start();
+
+ if (isRecording()) {
+ trackRecordingServiceConnection.startAndBind();
+ }
+
+ Intent intent = getIntent();
+ long trackId = intent.getLongExtra(TRACK_ID, -1L);
+ if (trackId != -1L) {
+ trackDataHub.loadTrack(trackId);
+ return;
+ }
+
+ long waypointId = intent.getLongExtra(WAYPOINT_ID, -1L);
+ if (waypointId != -1L) {
+ Waypoint waypoint = MyTracksProviderUtils.Factory.get(this).getWaypoint(waypointId);
+ if (waypoint != null) {
+ trackId = waypoint.getTrackId();
+ trackDataHub.loadTrack(trackId);
+ MapActivity mapActivity = getMapActivity();
+ if (mapActivity != null) {
+ getTabHost().setCurrentTab(0);
+ mapActivity.showWaypoint(trackId, waypointId);
+ } else {
+ Log.e(TAG, "MapActivity is null");
+ }
+ return;
+ }
+ }
+
+ // No track id or waypoint id, return to the track list activity.
+ startTrackListActivity();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ trackRecordingServiceConnection.bindIfRunning();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ trackDataHub.stop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ trackRecordingServiceConnection.unbind();
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DIALOG_INSTALL_EARTH_ID:
+ return PlayTrackUtils.createInstallEarthDialog(this);
+ case DIALOG_DELETE_CURRENT_ID:
+ return DialogUtils.createConfirmationDialog(this,
+ R.string.track_detail_delete_confirm_message, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ deleteCurrentTrack();
+ startTrackListActivity();
+ }
+ });
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.track_detail, menu);
+ String fileTypes[] = getResources().getStringArray(R.array.file_types);
+ menu.findItem(R.id.menu_save_gpx).setTitle(getString(R.string.menu_save_format, fileTypes[0]));
+ menu.findItem(R.id.menu_save_kml).setTitle(getString(R.string.menu_save_format, fileTypes[1]));
+ menu.findItem(R.id.menu_save_csv).setTitle(getString(R.string.menu_save_format, fileTypes[2]));
+ menu.findItem(R.id.menu_save_tcx).setTitle(getString(R.string.menu_save_format, fileTypes[3]));
+
+ menu.findItem(R.id.menu_share_gpx).setTitle(getString(R.string.menu_share_file, fileTypes[0]));
+ menu.findItem(R.id.menu_share_kml).setTitle(getString(R.string.menu_share_file, fileTypes[1]));
+ menu.findItem(R.id.menu_share_csv).setTitle(getString(R.string.menu_share_file, fileTypes[2]));
+ menu.findItem(R.id.menu_share_tcx).setTitle(getString(R.string.menu_share_file, fileTypes[3]));
+
+ stopRecordingMenuItem = menu.findItem(R.id.menu_stop_recording);
+ insertMarkerMenuItem = menu.findItem(R.id.menu_insert_marker);
+ playMenuItem = menu.findItem(R.id.menu_play);
+ shareMenuItem = menu.findItem(R.id.menu_share);
+ sendGoogleMenuItem = menu.findItem(R.id.menu_send_google);
+ saveMenuItem = menu.findItem(R.id.menu_save);
+ editMenuItem = menu.findItem(R.id.menu_edit);
+ deleteMenuItem = menu.findItem(R.id.menu_delete);
+
+ updateMenu();
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ String currentTabTag = getTabHost().getCurrentTabTag();
+ menu.findItem(R.id.menu_chart_settings).setVisible(CHART_TAB_TAG.equals(currentTabTag));
+ menu.findItem(R.id.menu_my_location).setVisible(MAP_TAB_TAG.equals(currentTabTag));
+
+ // Set map or satellite mode
+ MapActivity mapActivity = getMapActivity();
+ boolean isSatelliteMode = mapActivity != null ? mapActivity.isSatelliteView() : false;
+ menu.findItem(R.id.menu_satellite_mode).setVisible(MAP_TAB_TAG.equals(currentTabTag))
+ .setTitle(isSatelliteMode ? R.string.menu_map_mode : R.string.menu_satellite_mode);
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ MapActivity mapActivity;
+ Intent intent;
+ long trackId = trackDataHub.getSelectedTrackId();
+ switch (item.getItemId()) {
+ case R.id.menu_stop_recording:
+ updateMenuItems(false);
+ stopRecording();
+ return true;
+ case R.id.menu_insert_marker:
+ // TODO: Add insert marker when updating WaypointList to ICS
+ return true;
+ case R.id.menu_play:
+ if (PlayTrackUtils.isEarthInstalled(this)) {
+ PlayTrackUtils.playTrack(this, trackId);
+ } else {
+ showDialog(DIALOG_INSTALL_EARTH_ID);
+ }
+ return true;
+ case R.id.menu_share_map:
+ intent = new Intent(this, UploadServiceChooserActivity.class)
+ .putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, false, false));
+ startActivity(intent);
+ return true;
+ case R.id.menu_share_fusion_table:
+ intent = new Intent(this, UploadServiceChooserActivity.class)
+ .putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, false, true, false));
+ startActivity(intent);
+ return true;
+ case R.id.menu_share_gpx:
+ startSaveActivity(trackId, TrackFileFormat.GPX, true);
+ return true;
+ case R.id.menu_share_kml:
+ startSaveActivity(trackId, TrackFileFormat.KML, true);
+ return true;
+ case R.id.menu_share_csv:
+ startSaveActivity(trackId, TrackFileFormat.CSV, true);
+ return true;
+ case R.id.menu_share_tcx:
+ startSaveActivity(trackId, TrackFileFormat.TCX, true);
+ return true;
+ case R.id.menu_markers:
+ intent = new Intent(this, WaypointsList.class)
+ .putExtra("trackid", trackDataHub.getSelectedTrackId());
+ startActivityForResult(intent, Constants.SHOW_WAYPOINT);
+ return true;
+ case R.id.menu_send_google:
+ intent = new Intent(this, UploadServiceChooserActivity.class)
+ .putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, true, true));
+ startActivity(intent);
+ return true;
+ case R.id.menu_save_gpx:
+ startSaveActivity(trackId, TrackFileFormat.GPX, false);
+ return true;
+ case R.id.menu_save_kml:
+ startSaveActivity(trackId, TrackFileFormat.KML, false);
+ return true;
+ case R.id.menu_save_csv:
+ startSaveActivity(trackId, TrackFileFormat.CSV, false);
+ return true;
+ case R.id.menu_save_tcx:
+ startSaveActivity(trackId, TrackFileFormat.TCX, false);
+ return true;
+ case R.id.menu_edit:
+ startActivity(new Intent(this, TrackEditActivity.class)
+ .putExtra(TrackEditActivity.TRACK_ID, trackId));
+ return true;
+ case R.id.menu_delete:
+ showDialog(DIALOG_DELETE_CURRENT_ID);
+ return true;
+ case R.id.menu_my_location:
+ mapActivity = getMapActivity();
+ if (mapActivity != null) {
+ mapActivity.showMyLocation();
+ }
+ return true;
+ case R.id.menu_satellite_mode:
+ mapActivity = getMapActivity();
+ if (mapActivity != null) {
+ mapActivity.setSatelliteView(!mapActivity.isSatelliteView());
+ }
+ return true;
+ case R.id.menu_chart_settings:
+ ChartActivity chartActivity = getChartActivity();
+ if (chartActivity != null) {
+ chartActivity.showChartSettingsDialog();
+ }
+ return true;
+ case R.id.menu_sensor_state:
+ startActivity(new Intent(this, SensorStateActivity.class));
+ return true;
+ case R.id.menu_settings:
+ startActivity(new Intent(this, SettingsActivity.class));
+ return true;
+ case R.id.menu_help:
+ startActivity(new Intent(this, WelcomeActivity.class));
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, final Intent results) {
+ if (requestCode != Constants.SHOW_WAYPOINT) {
+ Log.d(TAG, "Invalid request code: " + requestCode);
+ return;
+ }
+ if (results != null) {
+ long waypointId = results.getLongExtra(WaypointDetails.WAYPOINT_ID_EXTRA, -1L);
+ if (waypointId != -1L) {
+ MapActivity mapActivity = (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
+ if (mapActivity != null) {
+ getTabHost().setCurrentTab(0);
+ mapActivity.showWaypoint(waypointId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (isRecording()) {
+ ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
+ .getServiceIfBound();
+ if (trackRecordingService == null) {
+ Log.e(TAG, "The track recording service is null");
+ return true;
+ }
+ boolean success = false;
+ try {
+ long waypointId = trackRecordingService.insertWaypoint(
+ WaypointCreationRequest.DEFAULT_STATISTICS);
+ if (waypointId != -1L) {
+ success = true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to insert waypoint", e);
+ }
+ Toast.makeText(this,
+ success ? R.string.marker_insert_success : R.string.marker_insert_error,
+ success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG)
+ .show();
+ return true;
+ }
+ }
+ return super.onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ navControls.show();
+ }
+ return false;
+ }
+
+ /**
+ * Updates the menu.
+ */
+ private void updateMenu() {
+ updateMenuItems(isRecording());
+ }
+
+ /**
+ * Updates the menu items.
+ *
+ * @param isRecording true if recording
+ */
+ private void updateMenuItems(boolean isRecording) {
+ if (stopRecordingMenuItem != null) {
+ stopRecordingMenuItem.setVisible(isRecording);
+ }
+ if (insertMarkerMenuItem != null) {
+ insertMarkerMenuItem.setVisible(isRecording);
+ }
+ if (playMenuItem != null) {
+ playMenuItem.setVisible(!isRecording);
+ }
+ if (shareMenuItem != null) {
+ shareMenuItem.setVisible(!isRecording);
+ }
+ if (sendGoogleMenuItem != null) {
+ sendGoogleMenuItem.setVisible(!isRecording);
+ }
+ if (saveMenuItem != null) {
+ saveMenuItem.setVisible(!isRecording);
+ }
+ if (editMenuItem != null) {
+ editMenuItem.setVisible(!isRecording);
+ }
+ if (deleteMenuItem != null) {
+ deleteMenuItem.setVisible(!isRecording);
+ }
+ }
+
+ /**
+ * Deletes the current track.
+ */
+ private void deleteCurrentTrack() {
+ long trackId = trackDataHub.getSelectedTrackId();
+ MyTracksProviderUtils.Factory.get(TrackDetailActivity.this).deleteTrack(trackId);
+ }
+
+ /**
+ * Starts the {@link TrackListActivity} and ends this activity.
+ */
+ private void startTrackListActivity() {
+ Intent intent = new Intent(this, TrackListActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ }
+
+ /**
+ * Starts the {@link SaveActivity} to save a track.
+ *
+ * @param trackId the track id
+ * @param trackFileFormat the track file format
+ * @param shareTrack true to share the track after saving
+ */
+ private void startSaveActivity(
+ long trackId, TrackFileFormat trackFileFormat, boolean shareTrack) {
+ Intent intent = new Intent(this, SaveActivity.class)
+ .putExtra(SaveActivity.EXTRA_TRACK_ID, trackId)
+ .putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat)
+ .putExtra(SaveActivity.EXTRA_SHARE_TRACK, shareTrack);
+ startActivity(intent);
+ }
+
+ /**
+ * Returns true if recording.
+ */
+ private boolean isRecording() {
+ return ServiceUtils.isRecording(
+ this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
+ }
+
+ /**
+ * Stops the recording and the track recording service connection and shows
+ * {@link TrackEditActivity}.
+ */
+ private void stopRecording() {
+ ITrackRecordingService trackRecordingService = trackRecordingServiceConnection
+ .getServiceIfBound();
+ if (trackRecordingService != null) {
+ try {
+ trackRecordingService.endCurrentTrack();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to stop recording.", e);
+ }
+ }
+ trackRecordingServiceConnection.stop();
+
+ long recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
+ if (recordingTrackId != -1L) {
+ Intent intent = new Intent(this, TrackEditActivity.class)
+ .putExtra(TrackEditActivity.SHOW_CANCEL, false)
+ .putExtra(TrackEditActivity.TRACK_ID, recordingTrackId);
+ startActivity(intent);
+ }
+ }
+
+ /**
+ * Gets the map activity, can be null.
+ */
+ private MapActivity getMapActivity() {
+ return (MapActivity) getLocalActivityManager().getActivity(MAP_TAB_TAG);
+ }
+
+ /**
+ * Gets the chart activity, can be null.
+ */
+ private ChartActivity getChartActivity() {
+ return (ChartActivity) getLocalActivityManager().getActivity(CHART_TAB_TAG);
+ }
+}
\ No newline at end of file
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackDetail.java b/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java
similarity index 84%
rename from MyTracks/src/com/google/android/apps/mytracks/TrackDetail.java
rename to MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java
index e44094ae7..bc21d80d0 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/TrackDetail.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java
@@ -35,12 +35,12 @@ import android.widget.EditText;
*
* @author Leif Hendrik Wilden
*/
-public class TrackDetail extends Activity implements OnClickListener {
+public class TrackEditActivity extends Activity implements OnClickListener {
public static final String TRACK_ID = "trackId";
public static final String SHOW_CANCEL = "showCancel";
- private static final String TAG = TrackDetail.class.getSimpleName();
+ private static final String TAG = TrackEditActivity.class.getSimpleName();
private Long trackId;
private MyTracksProviderUtils myTracksProviderUtils;
@@ -53,7 +53,7 @@ public class TrackDetail extends Activity implements OnClickListener {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
- setContentView(R.layout.track_detail);
+ setContentView(R.layout.track_edit);
trackId = getIntent().getLongExtra(TRACK_ID, -1);
if (trackId < 0) {
@@ -71,22 +71,22 @@ public class TrackDetail extends Activity implements OnClickListener {
return;
}
- trackName = (EditText) findViewById(R.id.track_detail_track_name);
+ trackName = (EditText) findViewById(R.id.track_edit_track_name);
trackName.setText(track.getName());
- activityType = (AutoCompleteTextView) findViewById(R.id.track_detail_activity_type);
+ activityType = (AutoCompleteTextView) findViewById(R.id.track_edit_activity_type);
activityType.setText(track.getCategory());
ArrayAdapter adapter = ArrayAdapter.createFromResource(
this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
activityType.setAdapter(adapter);
- trackDescription = (EditText) findViewById(R.id.track_detail_track_description);
+ trackDescription = (EditText) findViewById(R.id.track_edit_track_description);
trackDescription.setText(track.getDescription());
- Button save = (Button) findViewById(R.id.track_detail_save);
+ Button save = (Button) findViewById(R.id.track_edit_save);
save.setOnClickListener(this);
- Button cancel = (Button) findViewById(R.id.track_detail_cancel);
+ Button cancel = (Button) findViewById(R.id.track_edit_cancel);
if (getIntent().getBooleanExtra(SHOW_CANCEL, true)) {
cancel.setOnClickListener(this);
cancel.setVisibility(View.VISIBLE);
@@ -98,11 +98,11 @@ public class TrackDetail extends Activity implements OnClickListener {
@Override
public void onClick(View view) {
switch (view.getId()) {
- case R.id.track_detail_save:
+ case R.id.track_edit_save:
save();
finish();
break;
- case R.id.track_detail_cancel:
+ case R.id.track_edit_cancel:
finish();
break;
default:
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
index 7c8c366e7..c515cd16b 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
@@ -23,7 +23,9 @@ import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.ServiceUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
+import com.google.android.apps.mytracks.util.CheckUnitsUtils;
import com.google.android.apps.mytracks.util.DialogUtils;
+import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
@@ -33,12 +35,14 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Parcelable;
+import android.speech.tts.TextToSpeech;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -62,8 +66,13 @@ import android.widget.Toast;
public class TrackListActivity extends FragmentActivity {
private static final String TAG = TrackListActivity.class.getSimpleName();
- private static final String EXPORT_ALL_DIALOG_TAG = "exportAllDialog";
+ private static final String CHECK_UNITS_DIALOG_TAG = "checkUnitsDialog";
private static final String DELETE_ALL_DIALOG_TAG = "deleteAllDialog";
+ private static final String EULA_DIALOG_TAG = "eulaDialog";
+ private static final String EXPORT_ALL_DIALOG_TAG = "exportAllDialog";
+
+ private static final int WELCOME_ACTIVITY_REQUEST_CODE = 0;
+
private static final String[] PROJECTION = new String[] {
TracksColumns._ID,
TracksColumns.NAME,
@@ -73,18 +82,53 @@ public class TrackListActivity extends FragmentActivity {
TracksColumns.STARTTIME,
TracksColumns.DESCRIPTION };
- // Callback when the trackRecordingServiceConnection binding changes
+ // Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() {
@Override
public void run() {
- synchronized (trackRecordingServiceConnection) {
- if (startNewRecording) {
- startRecording();
- } else {
- updateMenu();
- adapter.notifyDataSetChanged();
- }
+ if (!startNewRecording) {
+ return;
}
+
+ ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
+ if (service == null) {
+ Log.d(TAG, "service not available to start a new recording");
+ return;
+ }
+ try {
+ recordingTrackId = service.startNewTrack();
+ startNewRecording = false;
+ Toast.makeText(
+ TrackListActivity.this, R.string.track_list_record_success, Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "Started a new recording");
+ } catch (Exception e) {
+ Toast.makeText(TrackListActivity.this, R.string.track_list_record_error, Toast.LENGTH_LONG)
+ .show();
+ Log.e(TAG, "Unable to start a new recording.", e);
+ }
+ }
+ };
+
+ /*
+ * Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
+ * Anonymous inner class will get garbage collected.
+ */
+ private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener =
+ new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ // Note that key can be null
+ if (getString(R.string.metric_units_key).equals(key)) {
+ metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
+ }
+ if (getString(R.string.recording_track_key).equals(key)) {
+ recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
+ if (isRecording()) {
+ trackRecordingServiceConnection.startAndBind();
+ }
+ updateMenu();
+ }
+ adapter.notifyDataSetChanged();
}
};
@@ -95,7 +139,7 @@ public class TrackListActivity extends FragmentActivity {
private ListView listView;
private ResourceCursorAdapter adapter;
- // True to start a new recording
+ // True to start a new recording.
private boolean startNewRecording = false;
private MenuItem recordTrack;
@@ -108,29 +152,15 @@ public class TrackListActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(R.layout.track_list);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(
this, bindChangedCallback);
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
- sharedPreferences.registerOnSharedPreferenceChangeListener(
- new SharedPreferences.OnSharedPreferenceChangeListener() {
- @Override
- public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
- if (key == null) {
- return;
- }
- if (key.equals(getString(R.string.metric_units_key))) {
- metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
- }
- if (key.equals(getString(R.string.recording_track_key))) {
- recordingTrackId = sharedPreferences.getLong(
- getString(R.string.recording_track_key), -1L);
- }
- adapter.notifyDataSetChanged();
- }
- });
+ sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
metricUnits = sharedPreferences.getBoolean(getString(R.string.metric_units_key), true);
recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
@@ -138,8 +168,9 @@ public class TrackListActivity extends FragmentActivity {
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
- setResult(Constants.SHOW_TRACK, new Intent().putExtra("trackid", id));
- finish();
+ Intent intent = new Intent(TrackListActivity.this, TrackDetailActivity.class)
+ .putExtra(TrackDetailActivity.TRACK_ID, id);
+ startActivity(intent);
}
});
adapter = new ResourceCursorAdapter(this, R.layout.track_list_item, null, 0) {
@@ -205,11 +236,23 @@ public class TrackListActivity extends FragmentActivity {
adapter.swapCursor(null);
}
});
+
+ if (!EulaUtils.getEulaValue(this)) {
+ new EulaDialogFragment().show(getSupportFragmentManager(), EULA_DIALOG_TAG);
+ }
}
@Override
protected void onStart() {
super.onStart();
+ if (isRecording()) {
+ trackRecordingServiceConnection.startAndBind();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
trackRecordingServiceConnection.bindIfRunning();
}
@@ -219,6 +262,24 @@ public class TrackListActivity extends FragmentActivity {
trackRecordingServiceConnection.unbind();
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, final Intent results) {
+ if (requestCode == WELCOME_ACTIVITY_REQUEST_CODE) {
+ if (!CheckUnitsUtils.getCheckUnitsValue(this)) {
+ /*
+ * See bug http://code.google.com/p/android/issues/detail?id=23761.
+ * Cannot use
+ * CheckUnitsDialogFragment().show(getSupportFragmentManager(),
+ * CHECK_UNITS_DIALOG_TAG). Need to use commitAllowingStateLoss with the
+ * support package.
+ */
+ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
+ fragmentTransaction.add(new CheckUnitsDialogFragment(), CHECK_UNITS_DIALOG_TAG);
+ fragmentTransaction.commitAllowingStateLoss();
+ }
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_list, menu);
@@ -239,8 +300,7 @@ public class TrackListActivity extends FragmentActivity {
* Updates the menu based on whether My Tracks is recording or not.
*/
private void updateMenu() {
- boolean isRecording = ServiceUtils.isRecording(
- this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
+ boolean isRecording = isRecording();
updateMenuItems(isRecording);
}
@@ -272,23 +332,11 @@ public class TrackListActivity extends FragmentActivity {
switch (item.getItemId()) {
case R.id.menu_record_track:
updateMenuItems(true);
- synchronized (trackRecordingServiceConnection) {
- startNewRecording = true;
- trackRecordingServiceConnection.startAndBind();
-
- /*
- * If the binding has happened, then invoke the callback to start a
- * new recording. If the binding hasn't happened, then invoking the
- * callback will have no effect. But when the binding occurs, the
- * callback will get invoked.
- */
- bindChangedCallback.run();
- }
+ startRecording();
return true;
case R.id.menu_stop_recording:
updateMenuItems(false);
stopRecording();
- trackRecordingServiceConnection.stop();
return true;
case R.id.menu_search:
return ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this);
@@ -316,27 +364,28 @@ public class TrackListActivity extends FragmentActivity {
}
}
+ /**
+ * Returns true if recording.
+ */
+ private boolean isRecording() {
+ return ServiceUtils.isRecording(
+ this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
+ }
+
/**
* Starts a new recording.
*/
private void startRecording() {
- ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
- if (service != null) {
- try {
- recordingTrackId = service.startNewTrack();
- startNewRecording = false;
- adapter.notifyDataSetChanged();
- Toast.makeText(TrackListActivity.this, R.string.track_record_success, Toast.LENGTH_SHORT)
- .show();
- Log.d(TAG, "Started a new recording");
- } catch (Exception e) {
- Toast.makeText(TrackListActivity.this, R.string.track_record_error, Toast.LENGTH_LONG)
- .show();
- Log.d(TAG, "Unable to start a new recording.", e);
- }
- } else {
- Log.d(TAG, "service not available to start a new recording");
- }
+ startNewRecording = true;
+ trackRecordingServiceConnection.startAndBind();
+
+ /*
+ * If the binding has happened, then invoke the callback to start a new
+ * recording. If the binding hasn't happened, then invoking the callback
+ * will have no effect. But when the binding occurs, the callback will get
+ * invoked.
+ */
+ bindChangedCallback.run();
}
/**
@@ -348,31 +397,124 @@ public class TrackListActivity extends FragmentActivity {
try {
service.endCurrentTrack();
if (recordingTrackId != -1L) {
- Intent intent = new Intent(this, TrackDetail.class)
- .putExtra(TrackDetail.SHOW_CANCEL, false)
- .putExtra(TrackDetail.TRACK_ID, recordingTrackId);
+ Intent intent = new Intent(this, TrackEditActivity.class)
+ .putExtra(TrackEditActivity.SHOW_CANCEL, false)
+ .putExtra(TrackEditActivity.TRACK_ID, recordingTrackId);
startActivity(intent);
}
recordingTrackId = -1L;
- adapter.notifyDataSetChanged();
} catch (Exception e) {
Log.d(TAG, "Unable to stop recording.", e);
}
}
+ trackRecordingServiceConnection.stop();
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH) {
+ if (ApiAdapterFactory.getApiAdapter().handleSearchKey(search)) {
+ return true;
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ /**
+ * Check Units DialogFragment.
+ */
+ public static class CheckUnitsDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ return new AlertDialog.Builder(getActivity()).setCancelable(true)
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ CheckUnitsUtils.setCheckUnitsValue(getActivity());
+ }
+ })
+ .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ CheckUnitsUtils.setCheckUnitsValue(getActivity());
+
+ int position = ((AlertDialog) dialog).getListView().getSelectedItemPosition();
+ SharedPreferences sharedPreferences = getActivity()
+ .getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(sharedPreferences.edit()
+ .putBoolean(getString(R.string.metric_units_key), position == 0));
+ }
+ })
+ .setSingleChoiceItems(new CharSequence[] { getString(R.string.preferred_units_metric),
+ getString(R.string.preferred_units_imperial) }, 0, null)
+ .setTitle(R.string.preferred_units_title)
+ .create();
+ }
+ }
+
+ /**
+ * Delete All DialogFragment.
+ */
+ public static class DeleteAllDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return DialogUtils.createConfirmationDialog(getActivity(),
+ R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ MyTracksProviderUtils.Factory.get(getActivity()).deleteAllTracks();
+ }
+ });
+ }
+ };
+
+ /**
+ * Eula DialogFragment.
+ */
+ public static class EulaDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setCancelable(true)
+ .setMessage(EulaUtils.getEulaMessage(getActivity()))
+ .setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ getActivity().finish();
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().finish();
+ }
+ })
+ .setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ EulaUtils.setEulaValue(getActivity());
+ getActivity().startActivityForResult(
+ new Intent(getActivity(), WelcomeActivity.class), WELCOME_ACTIVITY_REQUEST_CODE);
+ }
+ })
+ .setTitle(R.string.eula_title)
+ .create();
+ }
}
/**
* Export All DialogFragment.
*/
- private static class ExportAllDialogFragment extends DialogFragment {
+ public static class ExportAllDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
- String exportFileFormat = getString(R.string.track_list_export_file);
String fileTypes[] = getResources().getStringArray(R.array.file_types);
String[] choices = new String[fileTypes.length];
for (int i = 0; i < fileTypes.length; i++) {
- choices[i] = String.format(exportFileFormat, fileTypes[i]);
+ choices[i] = getString(R.string.menu_export_all_format, fileTypes[i]);
}
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@@ -389,42 +531,5 @@ public class TrackListActivity extends FragmentActivity {
.setTitle(R.string.menu_export_all)
.create();
}
- };
-
- /**
- * Delete All DialogFragment.
- */
- private static class DeleteAllDialogFragment extends DialogFragment {
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return DialogUtils.createConfirmationDialog(getActivity(),
- R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- MyTracksProviderUtils.Factory.get(getActivity()).deleteAllTracks();
- /*
- * TODO Verify that selected_track_key is still needed with the
- * ICS navigation design
- */
- SharedPreferences sharedPreferences = getActivity()
- .getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
- Editor editor = sharedPreferences.edit();
- // TODO: Go through data manager
- editor.putLong(getString(R.string.selected_track_key), -1L);
- ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
- }
- });
- }
- };
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_SEARCH) {
- if (ApiAdapterFactory.getApiAdapter().handleSearchKey(search)) {
- return true;
- }
- }
- return super.onKeyUp(keyCode, event);
}
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java
index b00aa3fe3..92cd7fb58 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/RestoreActivity.java
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.io.backup;
-import com.google.android.apps.mytracks.MyTracks;
+import com.google.android.apps.mytracks.TrackListActivity;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.maps.mytracks.R;
@@ -92,7 +92,8 @@ public class RestoreActivity extends Activity {
public void onAsyncTaskCompleted(boolean success, int messageId) {
removeDialog(DIALOG_PROGRESS_ID);
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
- startActivity(new Intent(this, MyTracks.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
+ startActivity(
+ new Intent(this, TrackListActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
finish();
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtils.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtils.java
index c3a29ce1d..142c20813 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtils.java
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.io.sendtogoogle;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.LocationUtils;
+import com.google.common.annotations.VisibleForTesting;
import android.location.Location;
import android.util.Log;
@@ -109,11 +110,12 @@ public class SendToGoogleUtils {
* @param segment the track segment
* @param splitTracks an array of track segments
*/
- private static void prepareTrackSegment(Track segment, ArrayList splitTracks) {
+ @VisibleForTesting
+ static boolean prepareTrackSegment(Track segment, ArrayList splitTracks) {
// Make sure the segment has at least 2 points
if (segment.getLocations().size() < 2) {
Log.d(TAG, "segment has less than 2 points");
- return;
+ return false;
}
// For a new segment, sets it stop time
@@ -128,5 +130,6 @@ public class SendToGoogleUtils {
LocationUtils.decimate(segment, 2.0);
splitTracks.add(segment);
+ return true;
}
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
index 45f7eb4a7..c72c1a19d 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
@@ -19,7 +19,7 @@ import static com.google.android.apps.mytracks.Constants.RESUME_TRACK_EXTRA_NAME
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
-import com.google.android.apps.mytracks.MyTracks;
+import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.content.DescriptionGenerator;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.MyTracksLocation;
@@ -484,9 +484,10 @@ public class TrackRecordingService extends Service {
Notification notification = new Notification(
R.drawable.arrow_320, null /* tickerText */,
System.currentTimeMillis());
+ Intent intent = new Intent(this, TrackDetailActivity.class)
+ .putExtra(TrackDetailActivity.TRACK_ID, recordingTrackId);
PendingIntent contentIntent = PendingIntent.getActivity(
- this, 0 /* requestCode */, new Intent(this, MyTracks.class),
- 0 /* flags */);
+ this, 0 /* requestCode */, intent, 0 /* flags */);
notification.setLatestEventInfo(this, getString(R.string.my_tracks_app_name),
getString(R.string.track_record_notification), contentIntent);
notification.flags += Notification.FLAG_NO_CLEAR;
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/CheckUnitsUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/CheckUnitsUtils.java
new file mode 100644
index 000000000..85fe5aa0b
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/CheckUnitsUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 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.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+/**
+ * Utilities for checking units.
+ *
+ * @author Jimmy Shih
+ */
+public class CheckUnitsUtils {
+
+ private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
+ private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
+
+ private CheckUnitsUtils() {}
+
+ public static boolean getCheckUnitsValue(Context context) {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(
+ CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
+ return sharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false);
+ }
+
+ public static void setCheckUnitsValue(Context context) {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(
+ CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
+ Editor editor = sharedPreferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/PlayTrackUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/PlayTrackUtils.java
index 2605904a5..28cdf39e3 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/PlayTrackUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/PlayTrackUtils.java
@@ -93,7 +93,7 @@ public class PlayTrackUtils {
public static Dialog createInstallEarthDialog(final Context context) {
return new AlertDialog.Builder(context)
.setCancelable(true)
- .setMessage(R.string.track_list_play_install_earth_message)
+ .setMessage(R.string.track_detail_install_earth_message)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
diff --git a/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java b/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java
index 9d123295d..40546740f 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/widgets/TrackWidgetProvider.java
@@ -19,7 +19,7 @@ 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.TrackDetailActivity;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
@@ -145,7 +145,10 @@ public class TrackWidgetProvider
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.track_widget);
// Make all of the stats open the mytracks activity.
- Intent intent = new Intent(context, MyTracks.class);
+ Intent intent = new Intent(context, TrackDetailActivity.class);
+ if (track != null) {
+ intent.putExtra(TrackDetailActivity.TRACK_ID, track.getId());
+ }
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.appwidget_track_statistics, pendingIntent);
diff --git a/MyTracksLib/.classpath b/MyTracksLib/.classpath
index 4aeffd3c3..0ca188f97 100644
--- a/MyTracksLib/.classpath
+++ b/MyTracksLib/.classpath
@@ -3,7 +3,6 @@
-
diff --git a/MyTracksTest/.classpath b/MyTracksTest/.classpath
index a59367532..17b7b5ec6 100644
--- a/MyTracksTest/.classpath
+++ b/MyTracksTest/.classpath
@@ -9,8 +9,6 @@
-
-
diff --git a/MyTracksTest/libs/AndroidMockRuntime.jar b/MyTracksTest/libs/AndroidMockRuntime.jar
deleted file mode 100644
index 40fa9e173..000000000
Binary files a/MyTracksTest/libs/AndroidMockRuntime.jar and /dev/null differ
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/MyTracksTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/TrackDetailActivityTest.java
similarity index 60%
rename from MyTracksTest/src/com/google/android/apps/mytracks/MyTracksTest.java
rename to MyTracksTest/src/com/google/android/apps/mytracks/TrackDetailActivityTest.java
index eecd804e3..697e63ea9 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/MyTracksTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/TrackDetailActivityTest.java
@@ -19,31 +19,28 @@ import com.google.android.apps.mytracks.services.ServiceUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.maps.mytracks.R;
-import android.app.Activity;
-import android.app.Instrumentation.ActivityMonitor;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
-import android.widget.Button;
import java.io.File;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * A unit test for {@link MyTracks} activity.
+ * A unit test for {@link TrackDetailActivity}.
*
* @author Bartlomiej Niechwiej
*/
-public class MyTracksTest extends ActivityInstrumentationTestCase2{
+public class TrackDetailActivityTest extends ActivityInstrumentationTestCase2{
private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection serviceConnection;
- public MyTracksTest() {
- super(MyTracks.class);
+ public TrackDetailActivityTest() {
+ super(TrackDetailActivity.class);
}
@Override
@@ -59,10 +56,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2{
// Check if not recording.
assertFalse(isRecording());
- assertEquals(-1, getRecordingTrackId());
- long selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
+ assertEquals(-1, getRecordingTrackId());
}
public void testInitialization_viewActionWithNoData() {
@@ -75,10 +69,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2{
// Check if not recording.
assertFalse(isRecording());
- assertEquals(-1, getRecordingTrackId());
- long selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
+ assertEquals(-1, getRecordingTrackId());
}
public void testInitialization_viewActionWithValidData() throws Exception {
@@ -97,10 +88,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2{
// Check if not recording.
assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId());
- long selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
-
+
// TODO: Finish this test.
}
@@ -117,75 +105,10 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2{
// Check if not recording.
assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId());
- long selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
-
+
// TODO: Finish this test.
}
- public void testRecording_startAndStop() throws Exception {
- assertInitialized();
-
- // Check if not recording.
- clearSelectedAndRecordingTracks();
- waitForIdle();
-
- assertFalse(isRecording());
- assertEquals(-1, getRecordingTrackId());
- long selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
-
- // Start a new track.
- getActivity().startRecording();
- serviceConnection.bindIfRunning();
- long recordingTrackId = awaitRecordingStatus(5000, true);
- assertTrue(recordingTrackId >= 0);
-
- // Wait until we are done and make sure that selectedTrack = recordingTrack.
- waitForIdle();
- assertEquals(recordingTrackId, getSharedPreferences().getLong(
- getActivity().getString(R.string.recording_track_key), -1));
- selectedTrackId = getSharedPreferences().getLong(
- getActivity().getString(R.string.selected_track_key), -1);
- assertEquals(recordingTrackId, selectedTrackId);
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
-
- // Watch for MyTracksDetails activity.
- ActivityMonitor monitor = getInstrumentation().addMonitor(
- TrackDetail.class.getName(), null, false);
-
- // Now, stop the track and make sure that it is still selected, but
- // no longer recording.
- getActivity().stopRecording();
-
- // Check if we got back MyTracksDetails activity.
- Activity activity = getInstrumentation().waitForMonitor(monitor);
- assertTrue(activity instanceof TrackDetail);
-
- // TODO: Update track name and other properties and test if they were
- // properly saved.
-
- // Simulate a click on Save button.
- final Button save = (Button) activity.findViewById(R.id.track_detail_save);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- save.performClick();
- }
- });
-
- // Check the remaining properties.
- recordingTrackId = awaitRecordingStatus(5000, false);
- assertEquals(-1, recordingTrackId);
- assertEquals(recordingTrackId, getRecordingTrackId());
- assertEquals(recordingTrackId, getSharedPreferences().getLong(
- getActivity().getString(R.string.recording_track_key), -1));
- // Make sure this is the same track as the last recording track ID.
- assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
- }
-
private void assertInitialized() {
assertNotNull(getActivity());
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/TrackStubUtils.java b/MyTracksTest/src/com/google/android/apps/mytracks/TrackStubUtils.java
index 5bc04ef01..2fc915d7a 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/TrackStubUtils.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/TrackStubUtils.java
@@ -31,9 +31,9 @@ import android.location.Location;
public class TrackStubUtils {
static final String LOCATION_PROVIDER = "gps";
- static final double INITIAL_LATITUDE = 22;
- static final double INITIAL_LONGITUDE = 22;
- static final double INITIAL_ALTITUDE = 22;
+ public static final double INITIAL_LATITUDE = 22;
+ public static final double INITIAL_LONGITUDE = 22;
+ public static final double INITIAL_ALTITUDE = 22;
static final float INITIAL_ACCURACY = 5;
static final float INITIAL_SPEED = 10;
static final float INITIAL_BEARING = 3.0f;
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtilsTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtilsTest.java
new file mode 100644
index 000000000..fa4629122
--- /dev/null
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/SendToGoogleUtilsTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 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.io.sendtogoogle;
+
+/**
+ * Tests the {@link SendToGoogleUtils}.
+ *
+ * @author Youtao Liu
+ */
+import com.google.android.apps.mytracks.TrackStubUtils;
+import com.google.android.apps.mytracks.content.MyTracksLocation;
+import com.google.android.apps.mytracks.content.Track;
+
+import android.location.Location;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class SendToGoogleUtilsTest extends TestCase {
+
+ private final static long TRACK_ID = 1L;
+ private final static String TRACK_CATEGORY = "trackCategory";
+ private final static String TRACK_NAME = "trackName";
+ private final static double DIFFERENCE = 0.02;
+ private final static double INVALID_LATITUDE = 91;
+
+ /**
+ * Tests the method
+ * {@link SendToGoogleUtils#prepareTrackSegment(Track, java.util.ArrayList)}
+ * when there is only one location in a segment.
+ */
+ public void testPrepareTrackSegment_onlyOneLocation() {
+ Track trackStub = TrackStubUtils.createTrack(1);
+ assertFalse(SendToGoogleUtils.prepareTrackSegment(trackStub, null));
+ }
+
+ /**
+ * Tests the method
+ * {@link SendToGoogleUtils#prepareTrackSegment(Track, ArrayList)} when there
+ * is no stop time.
+ */
+ public void testPrepareTrackSegment_noStopTime() {
+ Track trackStub = TrackStubUtils.createTrack(2);
+ assertEquals(-1L, trackStub.getStatistics().getStopTime());
+
+ ArrayList tracksArray = new ArrayList();
+ assertTrue(SendToGoogleUtils.prepareTrackSegment(trackStub, tracksArray));
+ assertEquals(trackStub, tracksArray.get(0));
+ // The stop time should be the time of last location
+ assertEquals(trackStub.getLocations().get(1).getTime(), trackStub.getStatistics().getStopTime());
+ }
+
+ /**
+ * Tests the method
+ * {@link SendToGoogleUtils#prepareTrackSegment(Track, java.util.ArrayList)}
+ * when there is stop time.
+ */
+ public void testPrepareTrackSegment_hasStopTime() {
+ Track trackStub = TrackStubUtils.createTrack(2);
+ // Gives a margin to make sure the this time will be not same with the last
+ // location in the track.
+ long stopTime = System.currentTimeMillis() + 1000;
+ trackStub.getStatistics().setStopTime(stopTime);
+
+ ArrayList tracksArray = new ArrayList();
+ SendToGoogleUtils.prepareTrackSegment(trackStub, tracksArray);
+ assertEquals(trackStub, tracksArray.get(0));
+ assertEquals(stopTime, trackStub.getStatistics().getStopTime());
+ }
+
+ /**
+ * Tests the method
+ * {@link SendToGoogleUtils#prepareLocations(Track, java.util.List)} .
+ */
+ public void testPrepareLocations() {
+ Track trackStub = TrackStubUtils.createTrack(2);
+ trackStub.setId(TRACK_ID);
+ trackStub.setName(TRACK_NAME);
+ trackStub.setCategory(TRACK_CATEGORY);
+
+ List locationsArray = new ArrayList();
+ // Adds 100 location to List.
+ for (int i = 0; i < 100; i++) {
+ // Use this variable as a flag to make all points in the track can be kept
+ // after run the LocationUtils#decimate(Track, double) with
+ // Ramer–Douglas–Peucker algorithm.
+ double latitude = TrackStubUtils.INITIAL_LATITUDE;
+ if (i % 2 == 0) {
+ latitude -= DIFFERENCE * (i % 10);
+ }
+ // Inserts 9 points which have wrong latitude, so would have 10 segments.
+ if (i % 10 == 0 && i > 0) {
+ MyTracksLocation location = TrackStubUtils.createMyTracksLocation();
+ location.setLatitude(INVALID_LATITUDE);
+ locationsArray.add(location);
+ } else {
+ locationsArray.add(TrackStubUtils.createMyTracksLocation(latitude,
+ TrackStubUtils.INITIAL_LONGITUDE + DIFFERENCE * (i % 10),
+ TrackStubUtils.INITIAL_ALTITUDE + DIFFERENCE * (i % 10)));
+ }
+ }
+ ArrayList result = SendToGoogleUtils.prepareLocations(trackStub, locationsArray);
+ assertEquals(10, result.size());
+ // Checks all segments.
+ for (int i = 0; i < 10; i++) {
+ Track segmentOne = result.get(i);
+ assertEquals(TRACK_ID, segmentOne.getId());
+ assertEquals(TRACK_NAME, segmentOne.getName());
+ assertEquals(TRACK_CATEGORY, segmentOne.getCategory());
+ }
+ // Checks all locations in the first segment.
+ for (int j = 0; j < 9; j++) {
+ double latitude = TrackStubUtils.INITIAL_LATITUDE;
+ if (j % 2 == 0) {
+ latitude -= DIFFERENCE * (j % 10);
+ }
+ Location oneLocation = result.get(0).getLocations().get(j);
+ assertEquals(TrackStubUtils.createMyTracksLocation().getAltitude() + DIFFERENCE * (j % 10),
+ oneLocation.getAltitude());
+ assertEquals(latitude, oneLocation.getLatitude());
+ assertEquals(TrackStubUtils.createMyTracksLocation().getLongitude() + DIFFERENCE * (j % 10),
+ oneLocation.getLongitude());
+ }
+ }
+}
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java
new file mode 100644
index 000000000..4025f5aa0
--- /dev/null
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2012 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.io.sendtogoogle;
+
+import com.google.android.maps.mytracks.R;
+import com.jayway.android.robotium.solo.Solo;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * Tests the {@link UploadResultActivity}.
+ *
+ * @author Youtao Liu
+ */
+public class UploadResultActivityTest extends
+ ActivityInstrumentationTestCase2 {
+
+ private Instrumentation instrumentation;
+ private UploadResultActivity uploadResultActivity;
+ private Solo solo;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ instrumentation = getInstrumentation();
+ solo = new Solo(instrumentation);
+ }
+
+ /**
+ * This method is necessary for ActivityInstrumentationTestCase2.
+ */
+ public UploadResultActivityTest() {
+ super(UploadResultActivity.class);
+ }
+
+ /**
+ * Checks the display of dialog when send to all and all sends are successful.
+ */
+ public void testAllSuccess() {
+ initialActivity(true, true, true, true, true, true);
+
+ HashSet stringHashSet = new HashSet();
+ ArrayList view = solo.getViews();
+ for (View oneView : view) {
+ if (oneView instanceof TextView && oneView.isShown()) {
+ stringHashSet.add((String) ((TextView) oneView).getText());
+ }
+ }
+
+ assertTrue(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.generic_success_title)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_maps)));
+ assertTrue(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_fusion_tables)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_docs)));
+ assertTrue(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_success_footer)));
+
+ assertFalse(stringHashSet
+ .contains(uploadResultActivity.getString(R.string.generic_error_title)));
+ assertFalse(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_error)));
+ }
+
+ /**
+ * Checks the display of dialog when send to all and all sends are failed.
+ */
+ public void testAllFailed() {
+ // Send all kinds but all failed.
+ initialActivity(true, true, true, false, false, false);
+ HashSet stringHashSet = new HashSet();
+ ArrayList view = solo.getViews();
+ for (View oneView : view) {
+ if (oneView instanceof TextView && oneView.isShown()) {
+ stringHashSet.add((String) ((TextView) oneView).getText());
+ }
+ }
+
+ assertTrue(stringHashSet.contains((Object) uploadResultActivity
+ .getString(R.string.generic_error_title)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_maps)));
+ assertTrue(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_fusion_tables)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_docs)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_error)));
+
+ assertFalse(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.generic_success_title)));
+ assertFalse(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_success_footer)));
+ }
+
+ /**
+ * Checks the display of dialog when match following items:
+ *
+ * Only send to Maps and Docs.
+ * Send to Maps successful.
+ * Send to Docs failed.
+ *
+ */
+ public void testPartialSuccess() {
+ initialActivity(true, false, true, true, false, false);
+
+ HashSet stringHashSet = new HashSet();
+ ArrayList view = solo.getViews();
+ for (View oneView : view) {
+ if (oneView instanceof TextView && oneView.isShown()) {
+ stringHashSet.add((String) ((TextView) oneView).getText());
+ }
+ }
+
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.generic_error_title)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_maps)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_docs)));
+ assertTrue(stringHashSet.contains(uploadResultActivity.getString(R.string.send_google_error)));
+
+ assertFalse(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_fusion_tables)));
+ assertFalse(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.generic_success_title)));
+ assertFalse(stringHashSet.contains(uploadResultActivity
+ .getString(R.string.send_google_success_footer)));
+ }
+
+ /**
+ * Initial a {@link SendRequest} and then initials a activity to be tested.
+ *
+ * @param isSendMaps
+ * @param isSendFusionTables
+ * @param isSendDocs
+ * @param isMapsSuccess
+ * @param isFusionTablesSuccess
+ * @param isDocsSuccess
+ */
+ private void initialActivity(boolean isSendMaps, boolean isSendFusionTables, boolean isSendDocs,
+ boolean isMapsSuccess, boolean isFusionTablesSuccess, boolean isDocsSuccess) {
+ Intent intent = new Intent();
+ SendRequest sendRequest = new SendRequest(1L, true, true, true);
+ sendRequest.setSendMaps(isSendMaps);
+ sendRequest.setSendFusionTables(isSendFusionTables);
+ sendRequest.setSendDocs(isSendDocs);
+ sendRequest.setMapsSuccess(isMapsSuccess);
+ sendRequest.setFusionTablesSuccess(isFusionTablesSuccess);
+ sendRequest.setDocsSuccess(isDocsSuccess);
+ intent.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
+ setActivityIntent(intent);
+ uploadResultActivity = this.getActivity();
+ }
+
+}