merge heads

This commit is contained in:
Jimmy Shih
2012-04-05 09:58:19 -07:00
35 changed files with 1053 additions and 1468 deletions
+8 -24
View File
@@ -190,28 +190,6 @@ limitations under the License.
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" /> android:value="com.google.android.apps.mytracks.SearchActivity" />
</activity> </activity>
<activity
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale"
android:name="com.google.android.apps.mytracks.MyTracks" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.track" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.waypoint" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" />
</activity>
<activity <activity
android:launchMode="singleTop" android:launchMode="singleTop"
android:name="com.google.android.apps.mytracks.SearchActivity" > android:name="com.google.android.apps.mytracks.SearchActivity" >
@@ -244,9 +222,10 @@ limitations under the License.
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" /> android:value="com.google.android.apps.mytracks.SearchActivity" />
</activity> </activity>
<activity android:name="com.google.android.apps.mytracks.TrackDetailActivity" />
<activity <activity
android:label="@string/track_detail_title" android:label="@string/track_edit_title"
android:name="com.google.android.apps.mytracks.TrackDetail" > android:name="com.google.android.apps.mytracks.TrackEditActivity" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.EDIT" />
@@ -257,6 +236,11 @@ limitations under the License.
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.google.android.apps.mytracks.TrackListActivity" > <activity android:name="com.google.android.apps.mytracks.TrackListActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data <meta-data
android:name="android.app.default_searchable" android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" /> android:value="com.google.android.apps.mytracks.SearchActivity" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

-19
View File
@@ -59,23 +59,4 @@ limitations under the License.
android:minHeight="80dp" android:minHeight="80dp"
android:layout_gravity="center"/> android:layout_gravity="center"/>
</LinearLayout> </LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="center"
android:paddingTop="10dp">
<LinearLayout
android:layout_width = "wrap_content"
android:layout_height= "wrap_content"
android:layout_weight="1"/>
<ImageButton
android:id="@+id/showOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:visibility="visible"
android:src="@android:drawable/ic_menu_more"/>
</LinearLayout>
</RelativeLayout> </RelativeLayout>
@@ -35,7 +35,7 @@ limitations under the License.
<TextView <TextView
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:text="@string/track_detail_track_name" android:text="@string/track_edit_track_name"
/> />
<LinearLayout <LinearLayout
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -53,8 +53,8 @@ limitations under the License.
/> />
<EditText <EditText
android:inputType="textAutoCorrect|textCapWords" android:inputType="textAutoCorrect|textCapWords"
android:hint="@string/track_detail_track_name" android:hint="@string/track_edit_track_name"
android:id="@+id/track_detail_track_name" android:id="@+id/track_edit_track_name"
android:imeOptions="actionNext" android:imeOptions="actionNext"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@@ -65,24 +65,24 @@ limitations under the License.
<TextView <TextView
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:text="@string/track_detail_activity_type" android:text="@string/track_edit_activity_type"
/> />
<AutoCompleteTextView <AutoCompleteTextView
android:id="@+id/track_detail_activity_type" android:id="@+id/track_edit_activity_type"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:hint="@string/track_detail_activity_type_hint" android:hint="@string/track_edit_activity_type_hint"
android:imeOptions="actionNext"/> android:imeOptions="actionNext"/>
<TextView <TextView
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:text="@string/track_detail_track_description" android:text="@string/track_edit_track_description"
/> />
<EditText <EditText
android:inputType="textAutoCorrect|textCapSentences" android:inputType="textAutoCorrect|textCapSentences"
android:hint="@string/track_detail_track_description" android:hint="@string/track_edit_track_description"
android:id="@+id/track_detail_track_description" android:id="@+id/track_edit_track_description"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="fill_parent" android:layout_width="fill_parent"
@@ -95,13 +95,13 @@ limitations under the License.
<LinearLayout style="@style/ButtonLayout"> <LinearLayout style="@style/ButtonLayout">
<LinearLayout style="@style/ButtonSpacer"/> <LinearLayout style="@style/ButtonSpacer"/>
<Button <Button
android:id="@+id/track_detail_save" android:id="@+id/track_edit_save"
android:layout_gravity="left" android:layout_gravity="left"
android:text="@string/generic_save" android:text="@string/generic_save"
style="@style/Button" style="@style/Button"
/> />
<Button <Button
android:id="@+id/track_detail_cancel" android:id="@+id/track_edit_cancel"
android:layout_gravity="right" android:layout_gravity="right"
android:text="@string/generic_cancel" android:text="@string/generic_cancel"
style="@style/Button" style="@style/Button"
+3 -15
View File
@@ -46,27 +46,15 @@ limitations under the License.
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
android:id="@+id/track_list_item_time" android:id="@+id/track_list_item_time"
android:layout_height="wrap_content" style="@style/TrackListItemStatsTextView" />
android:layout_width="wrap_content"
android:singleLine="true"
android:textColor="@color/green"
style="@android:style/TextAppearance.DeviceDefault.Small" />
<TextView <TextView
android:id="@+id/track_list_item_distance" android:id="@+id/track_list_item_distance"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_width="wrap_content" style="@style/TrackListItemStatsTextView" />
android:singleLine="true"
android:textColor="@color/green"
style="@android:style/TextAppearance.DeviceDefault.Small" />
<TextView <TextView
android:id="@+id/track_list_item_start" android:id="@+id/track_list_item_start"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:layout_width="wrap_content" style="@style/TrackListItemStatsTextView" />
android:singleLine="true"
android:textColor="@color/green"
style="@android:style/TextAppearance.DeviceDefault.Small" />
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/track_list_item_description" android:id="@+id/track_list_item_description"
@@ -15,48 +15,74 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:icon="@drawable/menu_record_track"
android:id="@+id/menu_record_track"
android:showAsAction="always"
android:title="@string/menu_record_track"/>
<item <item
android:icon="@drawable/menu_stop_recording" android:icon="@drawable/menu_stop_recording"
android:id="@+id/menu_stop_recording" android:id="@+id/menu_stop_recording"
android:showAsAction="always" android:showAsAction="always"
android:title="@string/menu_stop_recording"/> android:title="@string/menu_stop_recording"/>
<item <item
android:icon="@drawable/ic_menu_tracklist" android:icon="@drawable/menu_insert_marker"
android:id="@+id/menu_tracks" android:id="@+id/menu_insert_marker"
android:showAsAction="ifRoom" android:showAsAction="always"
android:title="@string/menu_tracks"/> android:title="@string/menu_insert_marker"/>
<item
android:icon="@drawable/menu_play"
android:id="@+id/menu_play"
android:showAsAction="always"
android:title="@string/menu_play"/>
<item
android:icon="@drawable/menu_share"
android:id="@+id/menu_share"
android:showAsAction="always"
android:title="@string/menu_share">
<menu>
<item
android:id="@+id/menu_share_map"
android:title="@string/menu_share_map"/>
<item
android:id="@+id/menu_share_fusion_table"
android:title="@string/menu_share_fusion_table"/>
<item android:id="@+id/menu_share_gpx"/>
<item android:id="@+id/menu_share_kml"/>
<item android:id="@+id/menu_share_csv"/>
<item android:id="@+id/menu_share_tcx"/>
</menu>
</item>
<item <item
android:icon="@drawable/ic_menu_waypoints" android:icon="@drawable/ic_menu_waypoints"
android:id="@+id/menu_markers" android:id="@+id/menu_markers"
android:showAsAction="ifRoom"
android:title="@string/menu_markers"/> android:title="@string/menu_markers"/>
<item <item
android:icon="@drawable/ic_menu_chartsettings" android:id="@+id/menu_send_google"
android:id="@+id/menu_chart_settings" android:title="@string/menu_send_google"/>
android:showAsAction="ifRoom" <item
android:title="@string/menu_chart_settings" android:id="@+id/menu_save"
android:visible="false"/> android:title="@string/menu_save">
<menu>
<item android:id="@+id/menu_save_gpx"/>
<item android:id="@+id/menu_save_kml"/>
<item android:id="@+id/menu_save_csv"/>
<item android:id="@+id/menu_save_tcx"/>
</menu>
</item>
<item
android:id="@+id/menu_edit"
android:title="@string/menu_edit"/>
<item
android:id="@+id/menu_delete"
android:title="@string/menu_delete"/>
<item <item
android:icon="@android:drawable/ic_menu_mylocation" android:icon="@android:drawable/ic_menu_mylocation"
android:id="@+id/menu_my_location" android:id="@+id/menu_my_location"
android:showAsAction="ifRoom" android:title="@string/menu_my_location"/>
android:title="@string/menu_my_location"
android:visible="false"/>
<item <item
android:icon="@android:drawable/ic_menu_mapmode" android:icon="@android:drawable/ic_menu_mapmode"
android:id="@+id/menu_satellite_mode" android:id="@+id/menu_satellite_mode"
android:showAsAction="ifRoom" android:title="@string/menu_satellite_mode"/>
android:title="@string/menu_satellite_mode"
android:visible="false"/>
<item <item
android:icon="@drawable/menu_search" android:icon="@drawable/ic_menu_chartsettings"
android:id="@+id/menu_search" android:id="@+id/menu_chart_settings"
android:title="@string/menu_search"/> android:title="@string/menu_chart_settings"/>
<item <item
android:icon="@drawable/ic_menu_sensorstatus" android:icon="@drawable/ic_menu_sensorstatus"
android:id="@+id/menu_sensor_state" android:id="@+id/menu_sensor_state"
@@ -65,9 +91,6 @@ limitations under the License.
android:icon="@drawable/menu_settings" android:icon="@drawable/menu_settings"
android:id="@+id/menu_settings" android:id="@+id/menu_settings"
android:title="@string/menu_settings"/> android:title="@string/menu_settings"/>
<item
android:id="@+id/menu_aggregated_statistics"
android:title="@string/menu_aggregated_statistics"/>
<item <item
android:icon="@drawable/menu_help" android:icon="@drawable/menu_help"
android:id="@+id/menu_help" android:id="@+id/menu_help"
+111 -171
View File
@@ -977,10 +977,25 @@ limitations under the License.
translation_description="In the menu, to delete all the tracks."> translation_description="In the menu, to delete all the tracks.">
Delete all Delete all
</string> </string>
<string name="menu_delete"
translation_description="In the menu, to delete a track or a marker.">
Delete
</string>
<string name="menu_edit"
translation_description="In the menu, to edit a track or a marker.">
Edit
</string>
<string name="menu_export_all" <string name="menu_export_all"
translation_description="In the menu, to export all the tracks to the SD card."> translation_description="In the menu, to export all the tracks to the SD card.">
Export all Export all
</string> </string>
<string name="menu_export_all_format"
translation_description="Under the 'Export all' menu option, the option to export all tracks as
a certain file format.
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.">
Export as %1$s
</string>
<string name="menu_help" <string name="menu_help"
translation_description="In the menu, to go to the help page."> translation_description="In the menu, to go to the help page.">
Help Help
@@ -989,6 +1004,10 @@ limitations under the License.
translation_description="In the menu, to import all the tracks from the SD card."> translation_description="In the menu, to import all the tracks from the SD card.">
Import all Import all
</string> </string>
<string name="menu_insert_marker"
translation_description="In the menu, to insert a marker.">
Insert marker
</string>
<string name="menu_map_mode" <string name="menu_map_mode"
translation_description="In the menu, to switch the display to the map mode."> translation_description="In the menu, to switch the display to the map mode.">
Map mode Map mode
@@ -1001,6 +1020,10 @@ limitations under the License.
translation_description="In the menu, to display the user's current location."> translation_description="In the menu, to display the user's current location.">
My location My location
</string> </string>
<string name="menu_play"
translation_description="In the menu, to play a track.">
Play
</string>
<string name="menu_record_track" <string name="menu_record_track"
translation_description="In the menu, to start recording a track."> translation_description="In the menu, to start recording a track.">
Record track Record track
@@ -1009,10 +1032,25 @@ limitations under the License.
translation_description="In the menu, to switch the display to the satellite mode."> translation_description="In the menu, to switch the display to the satellite mode.">
Satellite mode Satellite mode
</string> </string>
<string name="menu_save"
translation_description="In the menu, to save a track to the SD card.">
Save to SD card...
</string>
<string name="menu_save_format"
translation_description="Under the 'Save to SD card...' menu option, the option to save a track
as a certain file format.
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.">
As a %1$s file
</string>
<string name="menu_search" <string name="menu_search"
translation_description="In the menu, to start a textual search for tracks or waypoints."> translation_description="In the menu, to start a textual search for tracks or waypoints.">
Search Search
</string> </string>
<string name="menu_send_google"
translation_description="In the menu, to send a track to Google.">
Send to Google...
</string>
<string name="menu_sensor_state" <string name="menu_sensor_state"
translation_description="In the menu, to go to the sensor state page."> translation_description="In the menu, to go to the sensor state page.">
Sensor state Sensor state
@@ -1025,11 +1063,31 @@ limitations under the License.
translation_description="In the menu, to stop recording a track."> translation_description="In the menu, to stop recording a track.">
Stop recording Stop recording
</string> </string>
<string name="menu_tracks" <string name="menu_share"
translation_description="In the menu, to show a list of all the tracks."> translation_description="In the menu, to share a track with friends through other
Tracks applications.">
Share with friends...
</string> </string>
<string name="menu_share_file"
translation_description="Under the 'Share with friends...' menu option, the option to share a
track as a certain file format.
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.">
A %1$s file
</string>
<string name="menu_share_fusion_table"
translation_description="Under the 'Share with friends...' menu option, the option to save a
track to Google Fusion Tables and share the Fusion Table url with friends. 'Fusion Table' in
this context means a Google Fusion Table.">
Fusion Table
</string>
<string name="menu_share_map"
translation_description="Under the 'Share with friends...' menu option, the option to save a
track to Google Maps and share the Map url with friends. 'Map' in this context means a Google
Map.">
Map
</string>
<!-- My Location --> <!-- My Location -->
<string name="my_location_approximate_location" <string name="my_location_approximate_location"
translation_description="When displaying the user's current location, a status message translation_description="When displaying the user's current location, a status message
@@ -1200,6 +1258,13 @@ limitations under the License.
search results."> search results.">
No tracks or waypoints matched your search. No tracks or waypoints matched your search.
</string> </string>
<string name="search_track_location"
translation_description="In the 'Search Result' page, the text field indicating the track that a
waypoint belongs to.
%1$s: track name. E.g., 'Grand Canyon trail'">
In track \'%1$s\'.
</string>
<string name="search_voice_prompt" <string name="search_voice_prompt"
translation_description="Prompt shown when the user activates voice search, asking him to speak translation_description="Prompt shown when the user activates voice search, asking him to speak
what will be searched for."> what will be searched for.">
@@ -2238,196 +2303,71 @@ limitations under the License.
</string> </string>
<!-- Track --> <!-- Track -->
<string name="track_detail_activity_type" <string name="track_detail_delete_confirm_message"
translation_description="In the detail page for a track, the text field for the activity type, translation_description="When deleting a track, the confirmation message informing the user that
including for examples. the track and its markers will be permanently deleted from the phone.">
The track and its markers will be permanently deleted from the phone.
Screenshot: track_detail.png">
Activity type (e.g. hiking, cycling, running...)
</string> </string>
<string name="track_detail_activity_type_hint" <string name="track_detail_install_earth_message"
translation_description="In the detail page for a track, the hint in the 'Activity type' text translation_description="When playing a track, the message informing the user that he needs
field when it is empty. Google Earth to play the track and asking him if he wants to install Google Earth from the
Play Store.">
Screenshot: track_detail.png"> You need Google Earth to play the track. Do you want to install from the Play Store?
Activity type
</string>
<string name="track_detail_title"
translation_description="In the detail page for a track, the title of the page.
Screenshot: track_detail.png">
Track details
</string>
<string name="track_detail_track_description"
translation_description="In the detail page for a track, the text field for the track
description.
Screenshot: track_detail.png">
Track description
</string>
<string name="track_detail_track_name"
translation_description="In the detail page for a track, the text field for the track name.
Screenshot: track_detail.png">
Track name
</string> </string>
<string name="track_icon_description" <string name="track_icon_description"
translation_description="The accessibility description for the track icon."> translation_description="The accessibility description for the track icon.">
Track Track
</string> </string>
<string name="track_list_clear_map" <string name="track_edit_activity_type"
translation_description="In the context menu for a track, the menu option to clear the map of translation_description="In the page to edit a track, the title for the text field to edit the
any track. activity type, including some examples of activity types.">
Activity type (e.g. hiking, cycling, running...)
Screenshot: track_list_context_menu.png">
Clear map
</string> </string>
<string name="track_list_context_menu_title" <string name="track_edit_activity_type_hint"
translation_description="In the context menu for a track, the title indicating that all the menu translation_description="In the page to edit a track, the hint in the 'Activity type' text field
options below are for this track. when it is empty.">
Activity type
Screenshot: track_list_context_menu.png"> </string>
This track <string name="track_edit_title"
translation_description="The title of the page to edit a track.">
Edit track
</string>
<string name="track_edit_track_description"
translation_description="In the page to edit a track, the title for the text field to edit the
track description.">
Track description
</string>
<string name="track_edit_track_name"
translation_description="In the page to edit a track, the title for the text field to edit the
track name.">
Track name
</string> </string>
<string name="track_list_delete_all_confirm_message" <string name="track_list_delete_all_confirm_message"
translation_description="In the 'Tracks' page, when pressing the 'Delete all' button, the translation_description="When deleting all the tracks, the confirmation dialog message informing
confirmation dialog message informing the user that all tracks and markers will be permanently the user that all tracks and markers will be permanently deleted from the phone.">
deleted from the phone.
Screenshot: track_list_delete_all_confirm_message.png">
All tracks and markers will be permanently deleted from the phone. All tracks and markers will be permanently deleted from the phone.
</string> </string>
<string name="track_list_delete_track" <string name="track_list_record_error"
translation_description="In the context menu for a track, the menu option to delete the translation_description="When starting a new track recording, the error message informing the
track from the phone. user not able to record and asking the user to try again.">
Unable to start recording. No GPS location. Try again.
Screenshot: track_list_context_menu.png">
Delete from phone...
</string> </string>
<string name="track_list_delete_track_confirm_message" <string name="track_list_record_success"
translation_description="In the context menu for a track, when pressing the 'Delete from translation_description="When starting a new track recording, a status message indicating
phone...' option, the confirmation dialog message informing the user that the track and its success.">
markers will be permanently deleted from the phone. Now recording a new track.
Screenshot: track_list_delete_track_confirm_message.png">
The track and its markers will be permanently deleted from the phone.
</string>
<string name="track_list_edit_track"
translation_description="In the context menu for a track, the menu option to edit the track's
metadata (e.g., name, activity type, etc.).
Screenshot: track_list_context_menu.png">
Edit
</string>
<string name="track_list_export_file"
translation_description="In the 'Tracks' page, after pressing the 'Export all' button, the menu
option to export all the tracks as a certain file format
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.
Screenshot: track_list_export_file.png">
Export as %1$s
</string>
<string name="track_list_play"
translation_description="In the context menu for a track, the menu option to play the track.">
Play
</string>
<string name="track_list_play_install_earth_message"
translation_description="After pressing the menu option to play a track, the message informing
the user that he needs Google Earth to play the track and asking him if he wants to install
Google Earth from the Android Market.">
You need Google Earth to play the track. Do you want to install from the Market?
</string>
<string name="track_list_save_file"
translation_description="In the context menu for a track, under the 'Save to SD card...'
option, the menu option to save the track as a certain file format.
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.
Screenshot: track_list_save_file.png">
As a %1$s file
</string>
<string name="track_list_save_sd"
translation_description="In the context menu for a track, the menu option to save the track to
the SD card.
Screenshot: track_list_context_menu.png">
Save to SD card...
</string>
<string name="track_list_send_google"
translation_description="In the context menu for a track, the menu option to send the track to
Google.
Screenshot: track_list_context_menu.png">
Send to Google...
</string>
<string name="track_list_share_file"
translation_description="In the context menu for a track, under the 'Share with friends...'
option, the menu option to share the track as a certain file format.
%1$s: file format. 'GPX', 'KML', 'CSV', or 'TCX'.
Screenshot: track_list_share_options.png">
A %1$s file
</string>
<string name="track_list_share_fusion_table"
translation_description="In the context menu for a track, under the 'Share with friends...'
option, the menu option to save a track to Google Fusion Tables and share the Fusion Table
with friends. 'Fusion Table' in this context means a Google Fusion Table.">
Fusion Table
</string>
<string name="track_list_share_map"
translation_description="In the context menu for a track, under the 'Share with friends...'
option, the menu option to save a track to Google Maps and share the Map with friends. 'Map'
in this context means a Google Map.">
Map
</string>
<string name="track_list_share_track"
translation_description="In the context menu for a track, the menu option to share the track
with friends through other applications.
Screenshot: track_list_context_menu.png">
Share with friends...
</string>
<string name="track_list_show_on_map"
translation_description="In the context menu for a track, the menu option to show the track on
the map.
Screenshot: track_list_show_on_map.png">
Show on map
</string>
<string name="track_list_track_name"
translation_description="In the 'Search Result' page, the text field indicating the track that a
waypoint belongs to.
%1$s: track name. E.g., 'Grand Canyon trail'">
In track \'%1$s\'.
</string> </string>
<string name="track_name_format" <string name="track_name_format"
translation_description="When creating a new track, the string format to name the track. translation_description="When creating a new track, the string format to name the track.
%1$d: track number. E.g., '1'. %1$d: track number. E.g., '1'.">
Screenshot: track_name_format.png">
Track %1$d Track %1$d
</string> </string>
<string name="track_record_error"
translation_description="When starting a recording, message when not able to start due to no GPS
location and asking the user to try again.
Screenshot: track_record_error.png">
Unable to start recording. No GPS location. Try again.
</string>
<string name="track_record_notification" <string name="track_record_notification"
translation_description="When recording a track, the message on the My Tracks notification translation_description="When recording a track, the message on the My Tracks notification
informing the user that My Tracks is actively recording his track. informing the user that My Tracks is actively recording his track.">
Screenshot: track_record_notification.png">
Recording your track... Recording your track...
</string> </string>
<string name="track_record_success"
translation_description="When starting a new track recording, a status message indicating
success.
Screenshot: track_record_success.png">
Now recording a new track.
</string>
<!-- Unit --> <!-- Unit -->
<string name="unit_feet" <string name="unit_feet"
+9 -1
View File
@@ -125,7 +125,15 @@ limitations under the License.
<item name="android:textColor">@color/stat_label_text</item> <item name="android:textColor">@color/stat_label_text</item>
</style> </style>
<!-- Welcome page TextView --> <!-- The TextView showing the stats in the track list item -->
<style name="TrackListItemStatsTextView" parents="@android:style/TextAppearance.DeviceDefault.Small">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">@color/green</item>
</style>
<!-- Welcome page TextView -->
<style name="WelcomeTextView"> <style name="WelcomeTextView">
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">fill_parent</item> <item name="android:layout_width">fill_parent</item>
@@ -36,31 +36,15 @@ public abstract class Constants {
/* /*
* onActivityResult request codes: * onActivityResult request codes:
*/ */
public static final int SHOW_TRACK = 0; public static final int SHOW_WAYPOINT = 0;
public static final int SHOW_WAYPOINT = 1;
public static final int WELCOME = 2;
/* /*
* Context menu ids. Sorted alphabetically. * Context menu ids. Sorted alphabetically.
*/ */
public static final int MENU_CLEAR_MAP = 100; public static final int MENU_DELETE = 100;
public static final int MENU_DELETE = 101; public static final int MENU_EDIT = 101;
public static final int MENU_EDIT = 102; public static final int MENU_SHOW = 102;
public static final int MENU_PLAY = 103;
public static final int MENU_SAVE_CSV_FILE = 104;
public static final int MENU_SAVE_GPX_FILE = 105;
public static final int MENU_SAVE_KML_FILE = 106;
public static final int MENU_SAVE_TCX_FILE = 107;
public static final int MENU_SEND_TO_GOOGLE = 108;
public static final int MENU_SHARE = 109;
public static final int MENU_SHARE_CSV_FILE = 110;
public static final int MENU_SHARE_FUSION_TABLE = 111;
public static final int MENU_SHARE_GPX_FILE = 112;
public static final int MENU_SHARE_KML_FILE = 113;
public static final int MENU_SHARE_MAP = 114;
public static final int MENU_SHARE_TCX_FILE = 115;
public static final int MENU_SHOW = 116;
public static final int MENU_WRITE_TO_SD_CARD = 117;
/* /*
* Main screen tab tags: * Main screen tab tags:
*/ */
@@ -16,7 +16,6 @@
package com.google.android.apps.mytracks; package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.util.DialogUtils; import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.UriUtils; import com.google.android.apps.mytracks.util.UriUtils;
@@ -26,7 +25,6 @@ import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.ContentUris;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@@ -129,7 +127,7 @@ public class ImportActivity extends Activity {
.getQuantityString(R.plurals.importGpxFiles, totalCount, totalCount); .getQuantityString(R.plurals.importGpxFiles, totalCount, totalCount);
message = getString(R.string.import_success, successCount, totalFiles, path); message = getString(R.string.import_success, successCount, totalFiles, path);
} }
return new AlertDialog.Builder(this).setCancelable(true) return new AlertDialog.Builder(this)
.setCancelable(true) .setCancelable(true)
.setMessage(message) .setMessage(message)
.setOnCancelListener(new DialogInterface.OnCancelListener() { .setOnCancelListener(new DialogInterface.OnCancelListener() {
@@ -142,12 +140,10 @@ public class ImportActivity extends Activity {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
if (!importAll && trackId != -1L) { if (!importAll && trackId != -1L) {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(ImportActivity.this, TrackDetailActivity.class)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.setDataAndType( .putExtra(TrackDetailActivity.TRACK_ID, trackId);
ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId),
TracksColumns.CONTENT_ITEMTYPE);
startActivity(intent); startActivity(intent);
} }
finish(); finish();
@@ -23,42 +23,23 @@ import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType; import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
import com.google.android.apps.mytracks.content.TrackDataListener; import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint;
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.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.GeoRect; import com.google.android.apps.mytracks.util.GeoRect;
import com.google.android.apps.mytracks.util.LocationUtils; import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PlayTrackUtils;
import com.google.android.maps.GeoPoint; import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapController; import com.google.android.maps.MapController;
import com.google.android.maps.MapView; import com.google.android.maps.MapView;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.Location; import android.location.Location;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings; import android.provider.Settings;
import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.SubMenu;
import android.view.View; import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
@@ -76,9 +57,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
implements View.OnTouchListener, View.OnClickListener, implements View.OnTouchListener, View.OnClickListener,
TrackDataListener { TrackDataListener {
private static final int DIALOG_INSTALL_EARTH_ID = 0;
private static final int DIALOG_DELETE_CURRENT_ID = 1;
// Saved instance state keys: // Saved instance state keys:
// --------------------------- // ---------------------------
@@ -133,7 +111,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
private LinearLayout messagePane; private LinearLayout messagePane;
private TextView messageText; private TextView messageText;
private LinearLayout busyPane; private LinearLayout busyPane;
private ImageButton optionsBtn;
/** /**
* We are not displaying driving directions. Just an arbitrary track that is * We are not displaying driving directions. Just an arbitrary track that is
@@ -185,10 +162,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
messagePane = (LinearLayout) findViewById(R.id.messagepane); messagePane = (LinearLayout) findViewById(R.id.messagepane);
messageText = (TextView) findViewById(R.id.messagetext); messageText = (TextView) findViewById(R.id.messagetext);
busyPane = (LinearLayout) findViewById(R.id.busypane); busyPane = (LinearLayout) findViewById(R.id.busypane);
optionsBtn = (ImageButton) findViewById(R.id.showOptions);
optionsBtn.setOnCreateContextMenuListener(contextMenuListener);
optionsBtn.setOnClickListener(this);
} }
@Override @Override
@@ -243,45 +216,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
super.onPause(); super.onPause();
} }
@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_list_delete_track_confirm_message,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
long trackId = dataHub.getSelectedTrackId();
MyTracksProviderUtils.Factory.get(MapActivity.this).deleteTrack(trackId);
// If the deleted track was selected, unselect it.
String selectedTrackKey = getString(R.string.selected_track_key);
SharedPreferences sharedPreferences = getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
if (sharedPreferences.getLong(selectedTrackKey, -1L) == trackId) {
Editor editor = sharedPreferences.edit().putLong(selectedTrackKey, -1L);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
}
});
default:
return null;
}
}
// Utility functions:
// -------------------
/**
* Shows the options button if a track is selected, or hide it if not.
*/
private void updateOptionsButton(boolean trackSelected) {
optionsBtn.setVisibility(
trackSelected ? View.VISIBLE : View.INVISIBLE);
}
/** /**
* Tests if a location is visible. * Tests if a location is visible.
* *
@@ -451,8 +385,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
@Override @Override
public void run() { public void run() {
boolean trackSelected = track != null; boolean trackSelected = track != null;
updateOptionsButton(trackSelected);
mapOverlay.setTrackDrawingEnabled(trackSelected); mapOverlay.setTrackDrawingEnabled(trackSelected);
if (trackSelected) { if (trackSelected) {
@@ -473,133 +405,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
}); });
} }
private final OnCreateContextMenuListener contextMenuListener =
new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.setHeaderTitle(R.string.track_list_context_menu_title);
menu.add(Menu.NONE, Constants.MENU_EDIT, Menu.NONE, R.string.track_list_edit_track);
if (!dataHub.isRecordingSelected()) {
String saveFileFormat = getString(R.string.track_list_save_file);
String shareFileFormat = getString(R.string.track_list_share_file);
String fileTypes[] = getResources().getStringArray(R.array.file_types);
menu.add(Menu.NONE, Constants.MENU_PLAY, Menu.NONE, R.string.track_list_play);
menu.add(Menu.NONE, Constants.MENU_SEND_TO_GOOGLE, Menu.NONE,
R.string.track_list_send_google);
SubMenu share = menu.addSubMenu(
Menu.NONE, Constants.MENU_SHARE, Menu.NONE, R.string.track_list_share_track);
share.add(
Menu.NONE, Constants.MENU_SHARE_MAP, Menu.NONE, R.string.track_list_share_map);
share.add(Menu.NONE, Constants.MENU_SHARE_FUSION_TABLE, Menu.NONE,
R.string.track_list_share_fusion_table);
share.add(Menu.NONE, Constants.MENU_SHARE_GPX_FILE, Menu.NONE,
String.format(shareFileFormat, fileTypes[0]));
share.add(Menu.NONE, Constants.MENU_SHARE_KML_FILE, Menu.NONE,
String.format(shareFileFormat, fileTypes[1]));
share.add(Menu.NONE, Constants.MENU_SHARE_CSV_FILE, Menu.NONE,
String.format(shareFileFormat, fileTypes[2]));
share.add(Menu.NONE, Constants.MENU_SHARE_TCX_FILE, Menu.NONE,
String.format(shareFileFormat, fileTypes[3]));
SubMenu save = menu.addSubMenu(
Menu.NONE, Constants.MENU_WRITE_TO_SD_CARD, Menu.NONE, R.string.track_list_save_sd);
save.add(Menu.NONE, Constants.MENU_SAVE_GPX_FILE, Menu.NONE,
String.format(saveFileFormat, fileTypes[0]));
save.add(Menu.NONE, Constants.MENU_SAVE_KML_FILE, Menu.NONE,
String.format(saveFileFormat, fileTypes[1]));
save.add(Menu.NONE, Constants.MENU_SAVE_CSV_FILE, Menu.NONE,
String.format(saveFileFormat, fileTypes[2]));
save.add(Menu.NONE, Constants.MENU_SAVE_TCX_FILE, Menu.NONE,
String.format(saveFileFormat, fileTypes[3]));
menu.add(Menu.NONE, Constants.MENU_CLEAR_MAP, Menu.NONE, R.string.track_list_clear_map);
menu.add(Menu.NONE, Constants.MENU_DELETE, Menu.NONE, R.string.track_list_delete_track);
}
}
};
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
Intent intent;
long trackId = dataHub.getSelectedTrackId();
switch (item.getItemId()) {
case Constants.MENU_EDIT:
intent = new Intent(this, TrackDetail.class).putExtra(TrackDetail.TRACK_ID, trackId);
startActivity(intent);
return true;
case Constants.MENU_PLAY:
if (PlayTrackUtils.isEarthInstalled(this)) {
PlayTrackUtils.playTrack(this, trackId);
return true;
} else {
showDialog(DIALOG_INSTALL_EARTH_ID);
return true;
}
case Constants.MENU_SEND_TO_GOOGLE:
intent = new Intent(this, UploadServiceChooserActivity.class)
.putExtra(SendRequest.SEND_REQUEST_KEY, new SendRequest(trackId, true, true, true));
startActivity(intent);
return true;
case Constants.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 Constants.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 Constants.MENU_SHARE_GPX_FILE:
startSaveActivity(trackId, TrackFileFormat.GPX, true);
return true;
case Constants.MENU_SHARE_KML_FILE:
startSaveActivity(trackId, TrackFileFormat.KML, true);
return true;
case Constants.MENU_SHARE_CSV_FILE:
startSaveActivity(trackId, TrackFileFormat.CSV, true);
return true;
case Constants.MENU_SHARE_TCX_FILE:
startSaveActivity(trackId, TrackFileFormat.TCX, true);
return true;
case Constants.MENU_SAVE_GPX_FILE:
startSaveActivity(trackId, TrackFileFormat.GPX, false);
return true;
case Constants.MENU_SAVE_KML_FILE:
startSaveActivity(trackId, TrackFileFormat.KML, false);
return true;
case Constants.MENU_SAVE_CSV_FILE:
startSaveActivity(trackId, TrackFileFormat.CSV, false);
return true;
case Constants.MENU_SAVE_TCX_FILE:
startSaveActivity(trackId, TrackFileFormat.TCX, false);
return true;
case Constants.MENU_CLEAR_MAP:
dataHub.unloadCurrentTrack();
return true;
case Constants.MENU_DELETE:
showDialog(DIALOG_DELETE_CURRENT_ID);
return true;
default:
return super.onMenuItemSelected(featureId, item);
}
}
/**
* 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 whether the map is currently in satellite view mode. * Returns whether the map is currently in satellite view mode.
*/ */
@@ -618,8 +423,6 @@ public class MapActivity extends com.google.android.maps.MapActivity
public void onClick(View v) { public void onClick(View v) {
if (v == messagePane) { if (v == messagePane) {
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)); startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
} else if (v == optionsBtn) {
optionsBtn.performLongClick();
} }
} }
@@ -1,172 +0,0 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.CHART_TAB_TAG;
import static com.google.android.apps.mytracks.Constants.MAP_TAB_TAG;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
/**
* Manage the application menus.
*
* @author Sandor Dornbush
*/
class MenuManager {
private final MyTracks activity;
private MenuItem recordTrack;
private MenuItem stopRecording;
public MenuManager(MyTracks activity) {
this.activity = activity;
}
/**
* Fills the menu with its initial options.
*
* @param menu the menu to fill
* @return true on success, false otherwise
*/
public boolean onCreateOptionsMenu(Menu menu) {
activity.getMenuInflater().inflate(R.menu.main, menu);
// TODO: Replace search button with search widget if API level >= 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.<br>
* TODO: also update the action items when
* <ul>
* <li>start/stop a recording from the home screen widget.<il>
* <li>receive a direct service call to start/stop a recording.<il>
* </ul>
*
* @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;
}
}
@@ -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);
}
}
@@ -17,6 +17,9 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.TrackDataHub; import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.services.RemoveTempFilesService; 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.app.Application;
import android.content.Intent; import android.content.Intent;
@@ -32,6 +35,10 @@ public class MyTracksApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
if (BuildConfig.DEBUG) {
ApiAdapterFactory.getApiAdapter().enableStrictMode();
}
AnalyticsUtils.sendPageViews(this, "/appstart");
startService(new Intent(this, RemoveTempFilesService.class)); startService(new Intent(this, RemoveTempFilesService.class));
} }
@@ -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.SearchEngine.SearchQuery;
import com.google.android.apps.mytracks.content.SearchEngineProvider; import com.google.android.apps.mytracks.content.SearchEngineProvider;
import com.google.android.apps.mytracks.content.Track; 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.Waypoint;
import com.google.android.apps.mytracks.content.WaypointsColumns;
import com.google.android.apps.mytracks.stats.TripStatistics; import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.ListActivity; import android.app.ListActivity;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.location.Location; import android.location.Location;
import android.location.LocationManager; import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.SearchRecentSuggestions; import android.provider.SearchRecentSuggestions;
import android.util.Log; import android.util.Log;
@@ -193,7 +189,7 @@ public class SearchActivity extends ListActivity {
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription()); resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription());
resultMap.put(CATEGORY_FIELD, waypoint.getCategory()); resultMap.put(CATEGORY_FIELD, waypoint.getCategory());
// In the same place as we show time for tracks, show the track name for waypoints. // 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(STATS_FIELD, StringUtils.formatDateTime(this, waypoint.getLocation().getTime()));
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId()); resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
resultMap.put(WAYPOINT_ID_FIELD, waypoint.getId()); resultMap.put(WAYPOINT_ID_FIELD, waypoint.getId());
@@ -251,15 +247,12 @@ public class SearchActivity extends ListActivity {
} }
private Intent createViewDataIntent(Map<String, Object> clickedData) { private Intent createViewDataIntent(Map<String, Object> 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)) { if (clickedData.containsKey(WAYPOINT_ID_FIELD)) {
long waypointId = (Long) clickedData.get(WAYPOINT_ID_FIELD); intent.putExtra(TrackDetailActivity.WAYPOINT_ID, (Long) clickedData.get(WAYPOINT_ID_FIELD));
Uri uri = ContentUris.withAppendedId(WaypointsColumns.CONTENT_URI, waypointId);
intent.setDataAndType(uri, WaypointsColumns.CONTENT_ITEMTYPE);
} else { } else {
long trackId = (Long) clickedData.get(TRACK_ID_FIELD); intent.putExtra(TrackDetailActivity.TRACK_ID, (Long) clickedData.get(TRACK_ID_FIELD));
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
} }
return intent; return intent;
} }
@@ -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);
}
}
@@ -35,12 +35,12 @@ import android.widget.EditText;
* *
* @author Leif Hendrik Wilden * @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 TRACK_ID = "trackId";
public static final String SHOW_CANCEL = "showCancel"; 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 Long trackId;
private MyTracksProviderUtils myTracksProviderUtils; private MyTracksProviderUtils myTracksProviderUtils;
@@ -53,7 +53,7 @@ public class TrackDetail extends Activity implements OnClickListener {
@Override @Override
protected void onCreate(Bundle bundle) { protected void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
setContentView(R.layout.track_detail); setContentView(R.layout.track_edit);
trackId = getIntent().getLongExtra(TRACK_ID, -1); trackId = getIntent().getLongExtra(TRACK_ID, -1);
if (trackId < 0) { if (trackId < 0) {
@@ -71,22 +71,22 @@ public class TrackDetail extends Activity implements OnClickListener {
return; return;
} }
trackName = (EditText) findViewById(R.id.track_detail_track_name); trackName = (EditText) findViewById(R.id.track_edit_track_name);
trackName.setText(track.getName()); 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()); activityType.setText(track.getCategory());
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line); this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
activityType.setAdapter(adapter); 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()); 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); 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)) { if (getIntent().getBooleanExtra(SHOW_CANCEL, true)) {
cancel.setOnClickListener(this); cancel.setOnClickListener(this);
cancel.setVisibility(View.VISIBLE); cancel.setVisibility(View.VISIBLE);
@@ -98,11 +98,11 @@ public class TrackDetail extends Activity implements OnClickListener {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.track_detail_save: case R.id.track_edit_save:
save(); save();
finish(); finish();
break; break;
case R.id.track_detail_cancel: case R.id.track_edit_cancel:
finish(); finish();
break; break;
default: default:
@@ -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.ServiceUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection; import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.util.ApiAdapterFactory; 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.DialogUtils;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -33,12 +35,14 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.speech.tts.TextToSpeech;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
@@ -62,8 +66,13 @@ import android.widget.Toast;
public class TrackListActivity extends FragmentActivity { public class TrackListActivity extends FragmentActivity {
private static final String TAG = TrackListActivity.class.getSimpleName(); 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 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[] { private static final String[] PROJECTION = new String[] {
TracksColumns._ID, TracksColumns._ID,
TracksColumns.NAME, TracksColumns.NAME,
@@ -73,18 +82,53 @@ public class TrackListActivity extends FragmentActivity {
TracksColumns.STARTTIME, TracksColumns.STARTTIME,
TracksColumns.DESCRIPTION }; TracksColumns.DESCRIPTION };
// Callback when the trackRecordingServiceConnection binding changes // Callback when the trackRecordingServiceConnection binding changes.
private final Runnable bindChangedCallback = new Runnable() { private final Runnable bindChangedCallback = new Runnable() {
@Override @Override
public void run() { public void run() {
synchronized (trackRecordingServiceConnection) { if (!startNewRecording) {
if (startNewRecording) { return;
startRecording();
} else {
updateMenu();
adapter.notifyDataSetChanged();
}
} }
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 ListView listView;
private ResourceCursorAdapter adapter; private ResourceCursorAdapter adapter;
// True to start a new recording // True to start a new recording.
private boolean startNewRecording = false; private boolean startNewRecording = false;
private MenuItem recordTrack; private MenuItem recordTrack;
@@ -108,29 +152,15 @@ public class TrackListActivity extends FragmentActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(R.layout.track_list); setContentView(R.layout.track_list);
trackRecordingServiceConnection = new TrackRecordingServiceConnection( trackRecordingServiceConnection = new TrackRecordingServiceConnection(
this, bindChangedCallback); this, bindChangedCallback);
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE); sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
sharedPreferences.registerOnSharedPreferenceChangeListener( sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
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();
}
});
metricUnits = sharedPreferences.getBoolean(getString(R.string.metric_units_key), true); metricUnits = sharedPreferences.getBoolean(getString(R.string.metric_units_key), true);
recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L); recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
@@ -138,8 +168,9 @@ public class TrackListActivity extends FragmentActivity {
listView.setOnItemClickListener(new OnItemClickListener() { listView.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
setResult(Constants.SHOW_TRACK, new Intent().putExtra("trackid", id)); Intent intent = new Intent(TrackListActivity.this, TrackDetailActivity.class)
finish(); .putExtra(TrackDetailActivity.TRACK_ID, id);
startActivity(intent);
} }
}); });
adapter = new ResourceCursorAdapter(this, R.layout.track_list_item, null, 0) { adapter = new ResourceCursorAdapter(this, R.layout.track_list_item, null, 0) {
@@ -205,11 +236,23 @@ public class TrackListActivity extends FragmentActivity {
adapter.swapCursor(null); adapter.swapCursor(null);
} }
}); });
if (!EulaUtils.getEulaValue(this)) {
new EulaDialogFragment().show(getSupportFragmentManager(), EULA_DIALOG_TAG);
}
} }
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
if (isRecording()) {
trackRecordingServiceConnection.startAndBind();
}
}
@Override
protected void onResume() {
super.onResume();
trackRecordingServiceConnection.bindIfRunning(); trackRecordingServiceConnection.bindIfRunning();
} }
@@ -219,6 +262,24 @@ public class TrackListActivity extends FragmentActivity {
trackRecordingServiceConnection.unbind(); 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 @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_list, 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. * Updates the menu based on whether My Tracks is recording or not.
*/ */
private void updateMenu() { private void updateMenu() {
boolean isRecording = ServiceUtils.isRecording( boolean isRecording = isRecording();
this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
updateMenuItems(isRecording); updateMenuItems(isRecording);
} }
@@ -272,23 +332,11 @@ public class TrackListActivity extends FragmentActivity {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_record_track: case R.id.menu_record_track:
updateMenuItems(true); updateMenuItems(true);
synchronized (trackRecordingServiceConnection) { startRecording();
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();
}
return true; return true;
case R.id.menu_stop_recording: case R.id.menu_stop_recording:
updateMenuItems(false); updateMenuItems(false);
stopRecording(); stopRecording();
trackRecordingServiceConnection.stop();
return true; return true;
case R.id.menu_search: case R.id.menu_search:
return ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this); 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. * Starts a new recording.
*/ */
private void startRecording() { private void startRecording() {
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound(); startNewRecording = true;
if (service != null) { trackRecordingServiceConnection.startAndBind();
try {
recordingTrackId = service.startNewTrack(); /*
startNewRecording = false; * If the binding has happened, then invoke the callback to start a new
adapter.notifyDataSetChanged(); * recording. If the binding hasn't happened, then invoking the callback
Toast.makeText(TrackListActivity.this, R.string.track_record_success, Toast.LENGTH_SHORT) * will have no effect. But when the binding occurs, the callback will get
.show(); * invoked.
Log.d(TAG, "Started a new recording"); */
} catch (Exception e) { bindChangedCallback.run();
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");
}
} }
/** /**
@@ -348,31 +397,124 @@ public class TrackListActivity extends FragmentActivity {
try { try {
service.endCurrentTrack(); service.endCurrentTrack();
if (recordingTrackId != -1L) { if (recordingTrackId != -1L) {
Intent intent = new Intent(this, TrackDetail.class) Intent intent = new Intent(this, TrackEditActivity.class)
.putExtra(TrackDetail.SHOW_CANCEL, false) .putExtra(TrackEditActivity.SHOW_CANCEL, false)
.putExtra(TrackDetail.TRACK_ID, recordingTrackId); .putExtra(TrackEditActivity.TRACK_ID, recordingTrackId);
startActivity(intent); startActivity(intent);
} }
recordingTrackId = -1L; recordingTrackId = -1L;
adapter.notifyDataSetChanged();
} catch (Exception e) { } catch (Exception e) {
Log.d(TAG, "Unable to stop recording.", 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. * Export All DialogFragment.
*/ */
private static class ExportAllDialogFragment extends DialogFragment { public static class ExportAllDialogFragment extends DialogFragment {
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
String exportFileFormat = getString(R.string.track_list_export_file);
String fileTypes[] = getResources().getStringArray(R.array.file_types); String fileTypes[] = getResources().getStringArray(R.array.file_types);
String[] choices = new String[fileTypes.length]; String[] choices = new String[fileTypes.length];
for (int i = 0; i < fileTypes.length; i++) { 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) return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@@ -389,42 +531,5 @@ public class TrackListActivity extends FragmentActivity {
.setTitle(R.string.menu_export_all) .setTitle(R.string.menu_export_all)
.create(); .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);
} }
} }
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.io.backup; 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.apps.mytracks.util.DialogUtils;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
@@ -92,7 +92,8 @@ public class RestoreActivity extends Activity {
public void onAsyncTaskCompleted(boolean success, int messageId) { public void onAsyncTaskCompleted(boolean success, int messageId) {
removeDialog(DIALOG_PROGRESS_ID); removeDialog(DIALOG_PROGRESS_ID);
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show(); 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(); finish();
} }
@@ -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 static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants; 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.DescriptionGenerator;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl; import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.MyTracksLocation; import com.google.android.apps.mytracks.content.MyTracksLocation;
@@ -484,9 +484,10 @@ public class TrackRecordingService extends Service {
Notification notification = new Notification( Notification notification = new Notification(
R.drawable.arrow_320, null /* tickerText */, R.drawable.arrow_320, null /* tickerText */,
System.currentTimeMillis()); System.currentTimeMillis());
Intent intent = new Intent(this, TrackDetailActivity.class)
.putExtra(TrackDetailActivity.TRACK_ID, recordingTrackId);
PendingIntent contentIntent = PendingIntent.getActivity( PendingIntent contentIntent = PendingIntent.getActivity(
this, 0 /* requestCode */, new Intent(this, MyTracks.class), this, 0 /* requestCode */, intent, 0 /* flags */);
0 /* flags */);
notification.setLatestEventInfo(this, getString(R.string.my_tracks_app_name), notification.setLatestEventInfo(this, getString(R.string.my_tracks_app_name),
getString(R.string.track_record_notification), contentIntent); getString(R.string.track_record_notification), contentIntent);
notification.flags += Notification.FLAG_NO_CLEAR; notification.flags += Notification.FLAG_NO_CLEAR;
@@ -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);
}
}
@@ -93,7 +93,7 @@ public class PlayTrackUtils {
public static Dialog createInstallEarthDialog(final Context context) { public static Dialog createInstallEarthDialog(final Context context) {
return new AlertDialog.Builder(context) return new AlertDialog.Builder(context)
.setCancelable(true) .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) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override @Override
@@ -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.SETTINGS_NAME;
import static com.google.android.apps.mytracks.Constants.TAG; 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.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns; 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); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.track_widget);
// Make all of the stats open the mytracks activity. // 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); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.appwidget_track_statistics, pendingIntent); views.setOnClickPendingIntent(R.id.appwidget_track_statistics, pendingIntent);
@@ -19,31 +19,28 @@ import com.google.android.apps.mytracks.services.ServiceUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection; import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.maps.mytracks.R; import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.Instrumentation.ActivityMonitor;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.Editor;
import android.net.Uri; import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2; import android.test.ActivityInstrumentationTestCase2;
import android.widget.Button;
import java.io.File; import java.io.File;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* A unit test for {@link MyTracks} activity. * A unit test for {@link TrackDetailActivity}.
* *
* @author Bartlomiej Niechwiej * @author Bartlomiej Niechwiej
*/ */
public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{ public class TrackDetailActivityTest extends ActivityInstrumentationTestCase2<TrackDetailActivity>{
private SharedPreferences sharedPreferences; private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection serviceConnection; private TrackRecordingServiceConnection serviceConnection;
public MyTracksTest() { public TrackDetailActivityTest() {
super(MyTracks.class); super(TrackDetailActivity.class);
} }
@Override @Override
@@ -59,10 +56,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Check if not recording. // Check if not recording.
assertFalse(isRecording()); assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId()); assertEquals(-1, getRecordingTrackId());
long selectedTrackId = getSharedPreferences().getLong(
getActivity().getString(R.string.selected_track_key), -1);
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
} }
public void testInitialization_viewActionWithNoData() { public void testInitialization_viewActionWithNoData() {
@@ -75,10 +69,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Check if not recording. // Check if not recording.
assertFalse(isRecording()); assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId()); assertEquals(-1, getRecordingTrackId());
long selectedTrackId = getSharedPreferences().getLong(
getActivity().getString(R.string.selected_track_key), -1);
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
} }
public void testInitialization_viewActionWithValidData() throws Exception { public void testInitialization_viewActionWithValidData() throws Exception {
@@ -97,10 +88,7 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Check if not recording. // Check if not recording.
assertFalse(isRecording()); assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId()); assertEquals(-1, getRecordingTrackId());
long selectedTrackId = getSharedPreferences().getLong(
getActivity().getString(R.string.selected_track_key), -1);
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
// TODO: Finish this test. // TODO: Finish this test.
} }
@@ -117,75 +105,10 @@ public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
// Check if not recording. // Check if not recording.
assertFalse(isRecording()); assertFalse(isRecording());
assertEquals(-1, getRecordingTrackId()); assertEquals(-1, getRecordingTrackId());
long selectedTrackId = getSharedPreferences().getLong(
getActivity().getString(R.string.selected_track_key), -1);
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
// TODO: Finish this test. // 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() { private void assertInitialized() {
assertNotNull(getActivity()); assertNotNull(getActivity());