diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml
index 9ba432210..d64f76a7c 100755
--- a/MyTracks/AndroidManifest.xml
+++ b/MyTracks/AndroidManifest.xml
@@ -256,7 +256,7 @@ limitations under the License.
-
+
diff --git a/MyTracks/res/layout/mytracks_list.xml b/MyTracks/res/layout/mytracks_list.xml
deleted file mode 100644
index 74beadf5f..000000000
--- a/MyTracks/res/layout/mytracks_list.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MyTracks/res/layout/track_list.xml b/MyTracks/res/layout/track_list.xml
new file mode 100644
index 000000000..af2ae781c
--- /dev/null
+++ b/MyTracks/res/layout/track_list.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/MyTracks/res/layout/track_list_item.xml b/MyTracks/res/layout/track_list_item.xml
new file mode 100644
index 000000000..608174002
--- /dev/null
+++ b/MyTracks/res/layout/track_list_item.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MyTracks/res/menu/track_list.xml b/MyTracks/res/menu/track_list.xml
new file mode 100644
index 000000000..62ef77313
--- /dev/null
+++ b/MyTracks/res/menu/track_list.xml
@@ -0,0 +1,54 @@
+
+
+
\ No newline at end of file
diff --git a/MyTracks/res/values/colors.xml b/MyTracks/res/values/colors.xml
index 8bf50740b..9f396f181 100644
--- a/MyTracks/res/values/colors.xml
+++ b/MyTracks/res/values/colors.xml
@@ -17,6 +17,7 @@ limitations under the License.
#FF000000
#FF0000FF
+ #FF00FF00
#FFAAAAAA
#FFFF0000
#FFF0F0F0
diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml
index 32420774d..62d896b14 100644
--- a/MyTracks/res/values/strings.xml
+++ b/MyTracks/res/values/strings.xml
@@ -973,10 +973,22 @@ limitations under the License.
translation_description="In the menu, to change the chart settings.">
Chart settings
+
+ Delete all
+
+
+ Export all
+
Help
+
+ Import all
+
Map mode
@@ -2277,12 +2289,6 @@ limitations under the License.
Screenshot: track_list_context_menu.png">
This track
-
- Delete all
-
Edit
-
- Export all
-
Export as %1$s
-
- Import all
-
-
- Pick a track. Press & hold for more options.
-
Play
diff --git a/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java b/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java
index f9f624a39..6f23a39ad 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/MenuManager.java
@@ -118,7 +118,7 @@ class MenuManager {
return true;
}
case R.id.menu_tracks: {
- activity.startActivityForResult(new Intent(activity, TrackList.class),
+ activity.startActivityForResult(new Intent(activity, TrackListActivity.class),
Constants.SHOW_TRACK);
return true;
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java b/MyTracks/src/com/google/android/apps/mytracks/TrackList.java
deleted file mode 100644
index 0695071fa..000000000
--- a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java
+++ /dev/null
@@ -1,462 +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.TAG;
-
-import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
-import com.google.android.apps.mytracks.content.TracksColumns;
-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.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.apps.mytracks.util.StringUtils;
-import com.google.android.maps.mytracks.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.database.Cursor;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.SubMenu;
-import android.view.View;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.SimpleCursorAdapter;
-import android.widget.TextView;
-
-/**
- * A list activity displaying all the recorded tracks. There's a context
- * menu (via long press) displaying various options such as showing, editing,
- * deleting, sending to Google, or writing to SD card.
- *
- * @author Leif Hendrik Wilden
- */
-public class TrackList extends ListActivity
- implements SharedPreferences.OnSharedPreferenceChangeListener,
- View.OnClickListener {
-
- private static final int DIALOG_INSTALL_EARTH_ID = 0;
- private static final int DIALOG_EXPORT_ALL_ID = 1;
- private static final int DIALOG_DELETE_ALL_ID = 2;
- private static final int DIALOG_DELETE_CURRENT_ID = 3;
-
- private int contextPosition = -1;
- private long trackId = -1;
- private ListView listView = null;
- private boolean metricUnits = true;
-
- private Cursor tracksCursor = null;
-
- /**
- * The id of the currently recording track.
- */
- private long recordingTrackId = -1;
-
- 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);
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- contextPosition = info.position;
- trackId = TrackList.this.listView.getAdapter().getItemId(contextPosition);
- menu.add(Menu.NONE, Constants.MENU_SHOW, Menu.NONE, R.string.track_list_show_on_map);
- menu.add(Menu.NONE, Constants.MENU_EDIT, Menu.NONE, R.string.track_list_edit_track);
- if (!isRecording() || trackId != recordingTrackId) {
- 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_DELETE, Menu.NONE, R.string.track_list_delete_track);
- }
- }
- };
-
- private final Runnable serviceBindingChanged = new Runnable() {
- @Override
- public void run() {
- updateButtonsEnabled();
- }
- };
-
- private TrackRecordingServiceConnection serviceConnection;
- private SharedPreferences preferences;
-
- @Override
- public void onSharedPreferenceChanged(
- SharedPreferences sharedPreferences, String key) {
- if (key == null) {
- return;
- }
- if (key.equals(getString(R.string.metric_units_key))) {
- metricUnits = sharedPreferences.getBoolean(
- getString(R.string.metric_units_key), true);
- if (tracksCursor != null && !tracksCursor.isClosed()) {
- tracksCursor.requery();
- }
- }
- if (key.equals(getString(R.string.recording_track_key))) {
- recordingTrackId = sharedPreferences.getLong(
- getString(R.string.recording_track_key), -1);
- }
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- Intent result = new Intent();
- result.putExtra("trackid", id);
- setResult(Constants.SHOW_TRACK, result);
- finish();
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- Intent intent;
- switch (item.getItemId()) {
- case Constants.MENU_SHOW: {
- onListItemClick(null, null, 0, trackId);
- return true;
- }
- 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(TrackFileFormat.GPX, true);
- return true;
- case Constants.MENU_SHARE_KML_FILE:
- startSaveActivity(TrackFileFormat.KML, true);
- return true;
- case Constants.MENU_SHARE_CSV_FILE:
- startSaveActivity(TrackFileFormat.CSV, true);
- return true;
- case Constants.MENU_SHARE_TCX_FILE:
- startSaveActivity(TrackFileFormat.TCX, true);
- return true;
- case Constants.MENU_SAVE_GPX_FILE:
- startSaveActivity(TrackFileFormat.GPX, false);
- return true;
- case Constants.MENU_SAVE_KML_FILE:
- startSaveActivity(TrackFileFormat.KML, false);
- return true;
- case Constants.MENU_SAVE_CSV_FILE:
- startSaveActivity(TrackFileFormat.CSV, false);
- return true;
- case Constants.MENU_SAVE_TCX_FILE:
- startSaveActivity(TrackFileFormat.TCX, false);
- return true;
- case Constants.MENU_DELETE:
- showDialog(DIALOG_DELETE_CURRENT_ID);
- return true;
- default:
- Log.w(TAG, "Unknown menu item: " + item.getItemId() + "(" + item.getTitle() + ")");
- return super.onMenuItemSelected(featureId, item);
- }
- }
-
- /**
- * Starts the {@link SaveActivity} to save trackId.
- *
- * @param trackFileFormat the track file format
- * @param shareTrack true to share the track after saving
- */
- private void startSaveActivity(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);
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.tracklist_btn_delete_all:
- showDialog(DIALOG_DELETE_ALL_ID);
- break;
- case R.id.tracklist_btn_export_all:
- showDialog(DIALOG_EXPORT_ALL_ID);
- break;
- case R.id.tracklist_btn_import_all:
- Intent intent = new Intent(this, ImportActivity.class)
- .putExtra(ImportActivity.EXTRA_IMPORT_ALL, true);
- startActivity(intent);
- break;
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // We don't need a window title bar:
- requestWindowFeature(Window.FEATURE_NO_TITLE);
-
- setContentView(R.layout.mytracks_list);
-
- listView = getListView();
- listView.setOnCreateContextMenuListener(contextMenuListener);
-
- preferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
- serviceConnection = new TrackRecordingServiceConnection(this, serviceBindingChanged);
-
- View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
- deleteAll.setOnClickListener(this);
-
- View exportAll = findViewById(R.id.tracklist_btn_export_all);
- exportAll.setOnClickListener(this);
-
- updateButtonsEnabled();
-
- findViewById(R.id.tracklist_btn_import_all).setOnClickListener(this);
-
- preferences.registerOnSharedPreferenceChangeListener(this);
- metricUnits =
- preferences.getBoolean(getString(R.string.metric_units_key), true);
- recordingTrackId =
- preferences.getLong(getString(R.string.recording_track_key), -1);
-
- tracksCursor = getContentResolver().query(
- TracksColumns.CONTENT_URI, null, null, null, "_id DESC");
- startManagingCursor(tracksCursor);
- setListAdapter();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- serviceConnection.bindIfRunning();
- }
-
- @Override
- protected void onDestroy() {
- serviceConnection.unbind();
-
- super.onDestroy();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.search_only, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_search:
- onSearchRequested();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_INSTALL_EARTH_ID:
- return PlayTrackUtils.createInstallEarthDialog(this);
- case DIALOG_EXPORT_ALL_ID:
- String exportFileFormat = getString(R.string.track_list_export_file);
- String fileTypes[] = getResources().getStringArray(R.array.file_types);
- String[] choices = new String[fileTypes.length];
- for (int i = 0; i < fileTypes.length; i++) {
- choices[i] = String.format(exportFileFormat, fileTypes[i]);
- }
- return new AlertDialog.Builder(this)
- .setNegativeButton(R.string.generic_cancel, null)
- .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int index = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
- Intent intent = new Intent(TrackList.this, ExportActivity.class).putExtra(
- ExportActivity.EXTRA_TRACK_FILE_FORMAT,
- (Parcelable) TrackFileFormat.values()[index]);
- TrackList.this.startActivity(intent);
- }
- })
- .setSingleChoiceItems(choices, 0, null)
- .setTitle(R.string.track_list_export_all)
- .create();
- case DIALOG_DELETE_ALL_ID:
- return DialogUtils.createConfirmationDialog(this,
- R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- MyTracksProviderUtils.Factory.get(TrackList.this).deleteAllTracks();
- SharedPreferences sharedPreferences = 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);
- }
- });
- 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) {
- MyTracksProviderUtils.Factory.get(TrackList.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;
- }
- }
-
- private void updateButtonsEnabled() {
- View deleteAll = findViewById(R.id.tracklist_btn_delete_all);
- View exportAll = findViewById(R.id.tracklist_btn_export_all);
-
- boolean notRecording = !isRecording();
- deleteAll.setEnabled(notRecording);
- exportAll.setEnabled(notRecording);
- }
-
- private void setListAdapter() {
- // Get a cursor with all tracks
- SimpleCursorAdapter adapter = new SimpleCursorAdapter(
- this,
- R.layout.mytracks_list_item,
- tracksCursor,
- new String[] { TracksColumns.NAME, TracksColumns.STARTTIME,
- TracksColumns.TOTALDISTANCE, TracksColumns.DESCRIPTION,
- TracksColumns.CATEGORY },
- new int[] { R.id.track_list_item_name, R.id.track_list_item_time,
- R.id.track_list_item_stats, R.id.track_list_item_description,
- R.id.track_list_item_category });
-
- final int startTimeIdx =
- tracksCursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
- final int totalTimeIdx =
- tracksCursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
- final int totalDistanceIdx =
- tracksCursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
-
- adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
- @Override
- public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
- TextView textView = (TextView) view;
- if (columnIndex == startTimeIdx) {
- long time = cursor.getLong(startTimeIdx);
- textView.setText(StringUtils.formatDateTime(TrackList.this, time));
- } else if (columnIndex == totalDistanceIdx) {
- double totalDistance = cursor.getDouble(totalDistanceIdx);
- long totalTime = cursor.getLong(totalTimeIdx);
- String totalDistanceStr = StringUtils.formatTimeDistance(
- TrackList.this, totalTime, totalDistance, metricUnits);
- textView.setText(totalDistanceStr);
- } else {
- textView.setText(cursor.getString(columnIndex));
- if (textView.getText().length() < 1) {
- textView.setVisibility(View.GONE);
- } else {
- textView.setVisibility(View.VISIBLE);
- }
- }
- return true;
- }
-
- });
- setListAdapter(adapter);
- }
-
- private boolean isRecording() {
- return ServiceUtils.isRecording(TrackList.this, serviceConnection.getServiceIfBound(), preferences);
- }
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
new file mode 100644
index 000000000..728f685bc
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackListActivity.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.apps.mytracks;
+
+import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
+import com.google.android.apps.mytracks.content.TracksColumns;
+import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat;
+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.StringUtils;
+import com.google.android.maps.mytracks.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * An activity displaying a list of tracks.
+ *
+ * @author Leif Hendrik Wilden
+ */
+public class TrackListActivity extends Activity {
+
+ private static final String TAG = TrackListActivity.class.getSimpleName();
+ private static final int DIALOG_EXPORT_ALL_ID = 0;
+ private static final int DIALOG_DELETE_ALL_ID = 1;
+
+ // Callback when the trackRecordingServiceConnection binding changes
+ private final Runnable bindChangedCallback = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (trackRecordingServiceConnection) {
+ if (startNewRecording) {
+ startRecording();
+ } else {
+ updateMenu();
+ adapter.notifyDataSetChanged();
+ }
+ }
+ }
+ };
+
+ private TrackRecordingServiceConnection trackRecordingServiceConnection;
+ private SharedPreferences sharedPreferences;
+ private boolean metricUnits;
+ private long recordingTrackId;
+ private ListView listView;
+ private ResourceCursorAdapter adapter;
+
+ // True to start a new recording
+ private boolean startNewRecording = false;
+
+ private MenuItem recordTrack;
+ private MenuItem stopRecording;
+ private MenuItem search;
+ private MenuItem importAll;
+ private MenuItem exportAll;
+ private MenuItem deleteAll;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.track_list);
+
+ trackRecordingServiceConnection = new TrackRecordingServiceConnection(
+ this, bindChangedCallback);
+
+ sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
+ sharedPreferences.registerOnSharedPreferenceChangeListener(
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
+ if (key == null) {
+ return;
+ }
+ if (key.equals(getString(R.string.metric_units_key))) {
+ metricUnits = preferences.getBoolean(getString(R.string.metric_units_key), true);
+ }
+ if (key.equals(getString(R.string.recording_track_key))) {
+ recordingTrackId = sharedPreferences.getLong(
+ getString(R.string.recording_track_key), -1L);
+ }
+ adapter.notifyDataSetChanged();
+ }
+ });
+ metricUnits = sharedPreferences.getBoolean(getString(R.string.metric_units_key), true);
+ recordingTrackId = sharedPreferences.getLong(getString(R.string.recording_track_key), -1L);
+
+ MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(this);
+ Cursor trackCursor = myTracksProviderUtils.getTracksCursor(
+ null, null, TracksColumns._ID + " DESC");
+ startManagingCursor(trackCursor);
+ listView = (ListView) findViewById(R.id.track_list);
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ setResult(Constants.SHOW_TRACK, new Intent().putExtra("trackid", id));
+ finish();
+ }
+ });
+ adapter = new ResourceCursorAdapter(this, R.layout.track_list_item, trackCursor, true) {
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int idIndex = cursor.getColumnIndex(TracksColumns._ID);
+ int nameIndex = cursor.getColumnIndex(TracksColumns.NAME);
+ int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY);
+ int timeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
+ int distanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
+ int startIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
+ int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
+
+ boolean isRecording = cursor.getLong(idIndex) == recordingTrackId;
+
+ TextView name = (TextView) view.findViewById(R.id.track_list_item_name);
+ name.setText(cursor.getString(nameIndex));
+ name.setCompoundDrawablesWithIntrinsicBounds(
+ isRecording ? R.drawable.menu_record_track : R.drawable.track, 0, 0, 0);
+
+ TextView category = (TextView) view.findViewById(R.id.track_list_item_category);
+ category.setText(cursor.getString(categoryIndex));
+
+ TextView time = (TextView) view.findViewById(R.id.track_list_item_time);
+ time.setText(StringUtils.formatElapsedTime(cursor.getLong(timeIndex)));
+ time.setVisibility(isRecording ? View.GONE : View.VISIBLE);
+
+ TextView distance = (TextView) view.findViewById(R.id.track_list_item_distance);
+ distance.setText(StringUtils.formatDistance(
+ TrackListActivity.this, cursor.getDouble(distanceIndex), metricUnits));
+ distance.setVisibility(isRecording ? View.GONE : View.VISIBLE);
+
+ TextView start = (TextView) view.findViewById(R.id.track_list_item_start);
+ start.setText(
+ StringUtils.formatDateTime(TrackListActivity.this, cursor.getLong(startIndex)));
+ start.setVisibility(start.getText().equals(name.getText()) ? View.GONE : View.VISIBLE);
+
+ TextView description = (TextView) view.findViewById(R.id.track_list_item_description);
+ description.setText(cursor.getString(descriptionIndex));
+ description.setVisibility(description.getText().length() == 0 ? View.GONE : View.VISIBLE);
+ }
+ };
+ listView.setAdapter(adapter);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ trackRecordingServiceConnection.bindIfRunning();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ trackRecordingServiceConnection.unbind();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.track_list, menu);
+
+ recordTrack = menu.findItem(R.id.menu_record_track);
+ stopRecording = menu.findItem(R.id.menu_stop_recording);
+ search = menu.findItem(R.id.menu_search);
+ importAll = menu.findItem(R.id.menu_import_all);
+ exportAll = menu.findItem(R.id.menu_export_all);
+ deleteAll = menu.findItem(R.id.menu_delete_all);
+
+ ApiAdapterFactory.getApiAdapter().configureSearchWidget(this, search);
+ updateMenu();
+ return true;
+ }
+
+ /**
+ * Updates the menu based on whether My Tracks is recording or not.
+ */
+ private void updateMenu() {
+ boolean isRecording = ServiceUtils.isRecording(
+ this, trackRecordingServiceConnection.getServiceIfBound(), sharedPreferences);
+ updateMenuItems(isRecording);
+ }
+
+ /**
+ * Updates the menu items.
+ *
+ * @param isRecording true if recording
+ */
+ private void updateMenuItems(boolean isRecording) {
+ if (recordTrack != null) {
+ recordTrack.setVisible(!isRecording);
+ }
+ if (stopRecording != null) {
+ stopRecording.setVisible(isRecording);
+ }
+ if (importAll != null) {
+ importAll.setVisible(!isRecording);
+ }
+ if (exportAll != null) {
+ exportAll.setVisible(!isRecording);
+ }
+ if (deleteAll != null) {
+ deleteAll.setVisible(!isRecording);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_record_track:
+ updateMenuItems(true);
+ synchronized (trackRecordingServiceConnection) {
+ startNewRecording = true;
+ trackRecordingServiceConnection.startAndBind();
+
+ /*
+ * If the binding has happened, then invoke the callback to start a
+ * new recording. If the binding hasn't happened, then invoking the
+ * callback will have no effect. But when the binding occurs, the
+ * callback will get invoked.
+ */
+ bindChangedCallback.run();
+ }
+ return true;
+ case R.id.menu_stop_recording:
+ updateMenuItems(false);
+ stopRecording();
+ trackRecordingServiceConnection.stop();
+ return true;
+ case R.id.menu_search:
+ return ApiAdapterFactory.getApiAdapter().handleSearchMenuSelection(this);
+ case R.id.menu_import_all:
+ startActivity(new Intent(this, ImportActivity.class)
+ .putExtra(ImportActivity.EXTRA_IMPORT_ALL, true));
+ return true;
+ case R.id.menu_export_all:
+ showDialog(DIALOG_EXPORT_ALL_ID);
+ return true;
+ case R.id.menu_delete_all:
+ showDialog(DIALOG_DELETE_ALL_ID);
+ return true;
+ case R.id.menu_aggregated_statistics:
+ startActivity(new Intent(this, AggregatedStatsActivity.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 super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * Starts a new recording.
+ */
+ private void startRecording() {
+ ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
+ if (service != null) {
+ try {
+ recordingTrackId = service.startNewTrack();
+ startNewRecording = false;
+ adapter.notifyDataSetChanged();
+ Toast.makeText(TrackListActivity.this, R.string.track_record_success, Toast.LENGTH_SHORT)
+ .show();
+ Log.d(TAG, "Started a new recording");
+ } catch (Exception e) {
+ Toast.makeText(TrackListActivity.this, R.string.track_record_error, Toast.LENGTH_LONG)
+ .show();
+ Log.d(TAG, "Unable to start a new recording.", e);
+ }
+ } else {
+ Log.d(TAG, "service not available to start a new recording");
+ }
+ }
+
+ /**
+ * Stops the current recording.
+ */
+ private void stopRecording() {
+ ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
+ if (service != null) {
+ try {
+ service.endCurrentTrack();
+ if (recordingTrackId != -1L) {
+ Intent intent = new Intent(this, TrackDetail.class)
+ .putExtra(TrackDetail.SHOW_CANCEL, false)
+ .putExtra(TrackDetail.TRACK_ID, recordingTrackId);
+ startActivity(intent);
+ }
+ recordingTrackId = -1L;
+ adapter.notifyDataSetChanged();
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to stop recording.", e);
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DIALOG_EXPORT_ALL_ID:
+ return createExportAllDialog();
+ case DIALOG_DELETE_ALL_ID:
+ return DialogUtils.createConfirmationDialog(this,
+ R.string.track_list_delete_all_confirm_message, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ MyTracksProviderUtils.Factory.get(TrackListActivity.this).deleteAllTracks();
+ /*
+ * TODO Verify that selected_track_key is still needed with the
+ * ICS navigation design
+ */
+ Editor editor = sharedPreferences.edit();
+ // TODO: Go through data manager
+ editor.putLong(getString(R.string.selected_track_key), -1L);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
+ }
+ });
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Creates an export all dialog.
+ */
+ private AlertDialog createExportAllDialog() {
+ String exportFileFormat = getString(R.string.track_list_export_file);
+ String fileTypes[] = getResources().getStringArray(R.array.file_types);
+ String[] choices = new String[fileTypes.length];
+ for (int i = 0; i < fileTypes.length; i++) {
+ choices[i] = String.format(exportFileFormat, fileTypes[i]);
+ }
+ return new AlertDialog.Builder(this).setNegativeButton(R.string.generic_cancel, null)
+ .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int index = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
+ Intent intent = new Intent(TrackListActivity.this, ExportActivity.class).putExtra(
+ ExportActivity.EXTRA_TRACK_FILE_FORMAT,
+ (Parcelable) TrackFileFormat.values()[index]);
+ TrackListActivity.this.startActivity(intent);
+ }
+ })
+ .setSingleChoiceItems(choices, 0, null)
+ .setTitle(R.string.track_list_export_all)
+ .create();
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH) {
+ if (ApiAdapterFactory.getApiAdapter().handleSearchKey(search)) {
+ return true;
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/Api11Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api11Adapter.java
index d0527c63b..00396f572 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/Api11Adapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api11Adapter.java
@@ -16,18 +16,37 @@
package com.google.android.apps.mytracks.util;
+import android.annotation.TargetApi;
import android.app.Activity;
+import android.app.SearchManager;
+import android.content.Context;
+import android.view.MenuItem;
import android.view.Window;
+import android.widget.SearchView;
/**
* API level 11 specific implementation of the {@link ApiAdapter}.
*
* @author Jimmy Shih
*/
-public class Api11Adapter extends Api9Adapter {
+@TargetApi(11)
+public class Api11Adapter extends Api10Adapter {
@Override
public void showActionBar(Activity activity) {
activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
}
+
+ @Override
+ public void configureSearchWidget(Activity activity, MenuItem menuItem) {
+ SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
+ SearchView searchView = (SearchView) menuItem.getActionView();
+ searchView.setSearchableInfo(searchManager.getSearchableInfo(activity.getComponentName()));
+ }
+
+ @Override
+ public boolean handleSearchMenuSelection(Activity activity) {
+ // Returns false to allow the platform to expand the search widget.
+ return false;
+ }
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/Api14Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api14Adapter.java
new file mode 100644
index 000000000..ddd8376c7
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api14Adapter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.annotation.TargetApi;
+import android.view.MenuItem;
+
+/**
+ * API level 14 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+@TargetApi(14)
+public class Api14Adapter extends Api11Adapter {
+
+ @Override
+ public boolean handleSearchKey(MenuItem menuItem) {
+ menuItem.expandActionView();
+ return true;
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
index 1d6a6e8bd..1f7e8aefe 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
@@ -30,6 +30,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
+import android.view.MenuItem;
import android.view.Window;
import java.io.IOException;
@@ -108,4 +109,21 @@ public class Api7Adapter implements ApiAdapter {
// via the regular menu.
activity.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
+
+ @Override
+ public void configureSearchWidget(Activity activity, MenuItem menuItem) {
+ // Do nothing
+ }
+
+ @Override
+ public boolean handleSearchMenuSelection(Activity activity) {
+ activity.onSearchRequested();
+ return true;
+ }
+
+ @Override
+ public boolean handleSearchKey(MenuItem menuItem) {
+ // Return false and allow the framework to handle the search key.
+ return false;
+ }
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java
index 87ea57ab6..efdb86c25 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
+import android.view.MenuItem;
import java.io.IOException;
@@ -110,4 +111,31 @@ public interface ApiAdapter {
*/
public void showActionBar(Activity activity);
+ /**
+ * Configures the search widget.
+ *
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ * @param menuItem the search menu item
+ */
+ public void configureSearchWidget(Activity activity, MenuItem menuItem);
+
+ /**
+ * Handles the search menu selection. Returns true if handled.
+ *
+ * Due to changes in API level 11.
+ *
+ * @param activity the activity
+ */
+ public boolean handleSearchMenuSelection(Activity activity);
+
+ /**
+ * Handles the search key press. Returns true if handled.
+ *
+ * Due to changes in API level 14.
+ *
+ * @param menu the search menu
+ */
+ public boolean handleSearchKey(MenuItem menu);
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java
index 43ff5ee65..589aa7926 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java
@@ -31,7 +31,10 @@ public class ApiAdapterFactory {
*/
public static ApiAdapter getApiAdapter() {
if (apiAdapter == null) {
- if (Build.VERSION.SDK_INT >= 11) {
+ if (Build.VERSION.SDK_INT >= 14) {
+ apiAdapter = new Api14Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 11) {
apiAdapter = new Api11Adapter();
return apiAdapter;
} else if (Build.VERSION.SDK_INT >= 10) {