diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index 5ff5ac77e..7d997e93a 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -171,6 +171,7 @@ limitations under the License. + @@ -312,7 +313,7 @@ limitations under the License. - + diff --git a/MyTracks/src/com/google/android/apps/mytracks/ExportAllActivity.java b/MyTracks/src/com/google/android/apps/mytracks/ExportAllActivity.java new file mode 100644 index 000000000..8ff2ebb5b --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/ExportAllActivity.java @@ -0,0 +1,121 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks; + +import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat; +import com.google.android.maps.mytracks.R; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.widget.Toast; + +/** + * An activity to export all the tracks to the SD card. + * + * @author Sandor Dornbush + */ +public class ExportAllActivity extends Activity { + + public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format"; + + private static final int DIALOG_PROGRESS_ID = 0; + + private ExportAllAsyncTask exportAllAsyncTask; + private ProgressDialog progressDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Object retained = getLastNonConfigurationInstance(); + if (retained instanceof ExportAllAsyncTask) { + exportAllAsyncTask = (ExportAllAsyncTask) retained; + exportAllAsyncTask.setActivity(this); + } else { + Intent intent = getIntent(); + TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT); + + exportAllAsyncTask = new ExportAllAsyncTask(this, trackFileFormat); + exportAllAsyncTask.execute(); + } + } + + @Override + public Object onRetainNonConfigurationInstance() { + exportAllAsyncTask.setActivity(null); + return exportAllAsyncTask; + } + + @Override + protected Dialog onCreateDialog(int id) { + switch (id) { + case DIALOG_PROGRESS_ID: + progressDialog = new ProgressDialog(this); + progressDialog.setCancelable(true); + progressDialog.setIcon(android.R.drawable.ic_dialog_info); + progressDialog.setIndeterminate(true); + progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + exportAllAsyncTask.cancel(true); + finish(); + } + }); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setTitle(R.string.track_list_export_all); + return progressDialog; + default: + return null; + } + } + + /** + * Invokes when the associated AsyncTask completes. + * + * @param success true if the AsyncTask is successful + * @param messageId message id to display to user + */ + public void onAsyncTaskCompleted(boolean success, int messageId) { + removeDialog(DIALOG_PROGRESS_ID); + Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show(); + finish(); + } + + /** + * Shows the progress dialog. + */ + public void showProgressDialog() { + showDialog(DIALOG_PROGRESS_ID); + } + + /** + * Sets the progress dialog value. + * + * @param number the number of tracks exported + * @param max the maximum number of tracks + */ + public void setProgressDialogValue(int number, int max) { + if (progressDialog != null) { + progressDialog.setIndeterminate(false); + progressDialog.setMax(max); + progressDialog.setProgress(Math.min(number, max)); + } + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/ExportAllAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/ExportAllAsyncTask.java new file mode 100644 index 000000000..06bf1e1f0 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/ExportAllAsyncTask.java @@ -0,0 +1,167 @@ +/* + * 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; + +import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.content.TracksColumns; +import com.google.android.apps.mytracks.io.file.TrackWriter; +import com.google.android.apps.mytracks.io.file.TrackWriterFactory; +import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat; +import com.google.android.apps.mytracks.util.SystemUtils; +import com.google.android.maps.mytracks.R; + +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.AsyncTask; +import android.os.PowerManager.WakeLock; + +/** + * AsyncTask to export all the tracks to the SD card. + * + * @author Jimmy Shih + */ +public class ExportAllAsyncTask extends AsyncTask { + + private ExportAllActivity exportAllActivity; + private final TrackFileFormat trackFileFormat; + private final Context context; + private final MyTracksProviderUtils myTracksProviderUtils; + private WakeLock wakeLock; + private TrackWriter trackWriter; + + // true if the AsyncTask result is success + private boolean success; + + // true if the AsyncTask has completed + private boolean completed; + + // message id to return to the activity + private int messageId; + + /** + * Creates an AsyncTask. + * + * @param exportAllActivity the activity currently associated with this + * AsyncTask + * @param trackFileFormat the track file format + */ + public ExportAllAsyncTask(ExportAllActivity exportAllActivity, TrackFileFormat trackFileFormat) { + this.exportAllActivity = exportAllActivity; + this.trackFileFormat = trackFileFormat; + context = exportAllActivity.getApplicationContext(); + myTracksProviderUtils = MyTracksProviderUtils.Factory.get(exportAllActivity); + + SharedPreferences sharedPreferences = exportAllActivity.getSharedPreferences( + Constants.SETTINGS_NAME, Context.MODE_PRIVATE); + long recordingTrackId = sharedPreferences.getLong( + exportAllActivity.getString(R.string.recording_track_key), -1L); + + // Get the wake lock if not recording + if (recordingTrackId == -1L) { + wakeLock = SystemUtils.acquireWakeLock(exportAllActivity, wakeLock); + } + success = false; + completed = false; + messageId = R.string.export_error; + } + + /** + * Sets the current {@link ExportAllActivity} associated with this AyncTask. + * + * @param exportAllActivity the current {@link ExportAllActivity}, can be null + */ + public void setActivity(ExportAllActivity exportAllActivity) { + this.exportAllActivity = exportAllActivity; + if (completed && exportAllActivity != null) { + exportAllActivity.onAsyncTaskCompleted(success, messageId); + } + } + + @Override + protected void onPreExecute() { + if (exportAllActivity != null) { + exportAllActivity.showProgressDialog(); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + Cursor cursor = null; + try { + cursor = myTracksProviderUtils.getTracksCursor(null, null, TracksColumns._ID); + if (cursor == null) { + messageId = R.string.export_success; + return true; + } + int count = cursor.getCount(); + int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID); + for (int i = 0; i < count; i++) { + if (isCancelled()) { + return false; + } + cursor.moveToPosition(i); + long id = cursor.getLong(idIndex); + trackWriter = TrackWriterFactory.newWriter( + context, myTracksProviderUtils, id, trackFileFormat); + if (trackWriter == null) { + return false; + } + trackWriter.writeTrack(); + + if (!trackWriter.wasSuccess()) { + messageId = trackWriter.getErrorMessage(); + return false; + } + publishProgress(i + 1, count); + } + messageId = R.string.export_success; + return true; + } finally { + if (cursor != null) { + cursor.close(); + } + // Release the wake lock if obtained + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + } + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + if (exportAllActivity != null) { + exportAllActivity.setProgressDialogValue(values[0], values[1]); + } + } + + @Override + protected void onPostExecute(Boolean result) { + success = result; + completed = true; + if (exportAllActivity != null) { + exportAllActivity.onAsyncTaskCompleted(success, messageId); + } + } + + @Override + protected void onCancelled() { + if (trackWriter != null) { + trackWriter.stopWriteTrack(); + } + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java deleted file mode 100644 index ef6169658..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/ExportAllTracks.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.android.apps.mytracks; - -import com.google.android.apps.mytracks.content.MyTracksProviderUtils; -import com.google.android.apps.mytracks.content.TracksColumns; -import com.google.android.apps.mytracks.io.file.TrackWriter; -import com.google.android.apps.mytracks.io.file.TrackWriterFactory; -import com.google.android.apps.mytracks.io.file.TrackWriterFactory.TrackFileFormat; -import com.google.android.apps.mytracks.util.SystemUtils; -import com.google.android.maps.mytracks.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.PowerManager.WakeLock; -import android.util.Log; -import android.widget.Toast; - -/** - * A class that will export all tracks to the sd card. - * - * @author Sandor Dornbush - */ -public class ExportAllTracks { - // These must line up with the index in the array. - public static final int GPX_OPTION_INDEX = 0; - public static final int KML_OPTION_INDEX = 1; - public static final int CSV_OPTION_INDEX = 2; - public static final int TCX_OPTION_INDEX = 3; - - private final Activity activity; - private WakeLock wakeLock; - private ProgressDialog progress; - - private TrackFileFormat format = TrackFileFormat.GPX; - - private final DialogInterface.OnClickListener itemClick = - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case GPX_OPTION_INDEX: - format = TrackFileFormat.GPX; - break; - case KML_OPTION_INDEX: - format = TrackFileFormat.KML; - break; - case CSV_OPTION_INDEX: - format = TrackFileFormat.CSV; - break; - case TCX_OPTION_INDEX: - format = TrackFileFormat.TCX; - break; - default: - Log.w(Constants.TAG, "Unknown export format: " + which); - } - } - }; - - public ExportAllTracks(Activity activity) { - this.activity = activity; - Log.i(Constants.TAG, "ExportAllTracks: Starting"); - - String exportFileFormat = activity.getString(R.string.track_list_export_file); - String fileTypes[] = activity.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]); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.track_list_export_all); - builder.setSingleChoiceItems(choices, 0, itemClick); - builder.setPositiveButton(R.string.generic_ok, positiveClick); - builder.setNegativeButton(R.string.generic_cancel, null); - builder.show(); - } - - private final DialogInterface.OnClickListener positiveClick = - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - new Thread(runner, "ExportAllTracks").start(); - } - }; - - private final Runnable runner = new Runnable() { - public void run() { - aquireLocksAndExport(); - } - }; - - /** - * Makes sure that we keep the phone from sleeping. - * See if there is a current track. Aquire a wake lock if there is no - * current track. - */ - private void aquireLocksAndExport() { - SharedPreferences prefs = activity.getSharedPreferences( - Constants.SETTINGS_NAME, Context.MODE_PRIVATE); - long recordingTrackId = -1; - if (prefs != null) { - recordingTrackId = - prefs.getLong(activity.getString(R.string.recording_track_key), -1); - } - if (recordingTrackId != -1) { - wakeLock = SystemUtils.acquireWakeLock(activity, wakeLock); - } - - // Now we can safely export everything. - exportAll(); - - // Release the wake lock if we recorded one. - // TODO check what happens if we started recording after getting this lock. - if (wakeLock != null && wakeLock.isHeld()) { - wakeLock.release(); - Log.i(Constants.TAG, "ExportAllTracks: Releasing wake lock."); - } - Log.i(Constants.TAG, "ExportAllTracks: Done"); - showToast(R.string.export_success, Toast.LENGTH_SHORT); - } - - private void makeProgressDialog(final int trackCount) { - String exportMsg = activity.getString(R.string.track_list_export_all); - progress = new ProgressDialog(activity); - progress.setIcon(android.R.drawable.ic_dialog_info); - progress.setTitle(exportMsg); - progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - progress.setMax(trackCount); - progress.setProgress(0); - progress.show(); - } - - /** - * Actually export the tracks. - * This should be called after the wake locks have been aquired. - */ - private void exportAll() { - // Get a cursor over all tracks. - Cursor cursor = null; - try { - MyTracksProviderUtils providerUtils = - MyTracksProviderUtils.Factory.get(activity); - cursor = providerUtils.getTracksCursor(null, null, TracksColumns._ID); - if (cursor == null) { - return; - } - - final int trackCount = cursor.getCount(); - Log.i(Constants.TAG, - "ExportAllTracks: Exporting: " + cursor.getCount() + " tracks."); - int idxTrackId = cursor.getColumnIndexOrThrow(TracksColumns._ID); - activity.runOnUiThread(new Runnable() { - public void run() { - makeProgressDialog(trackCount); - } - }); - - for (int i = 0; cursor.moveToNext(); i++) { - final int status = i; - activity.runOnUiThread(new Runnable() { - public void run() { - synchronized (this) { - if (progress == null) { - return; - } - progress.setProgress(status); - } - } - }); - - long id = cursor.getLong(idxTrackId); - Log.i(Constants.TAG, "ExportAllTracks: exporting: " + id); - TrackWriter writer = - TrackWriterFactory.newWriter(activity, providerUtils, id, format); - if (writer == null) { - showToast(R.string.export_error, Toast.LENGTH_LONG); - return; - } - - writer.writeTrack(); - - if (!writer.wasSuccess()) { - // Abort the whole export on the first error. - showToast(writer.getErrorMessage(), Toast.LENGTH_LONG); - return; - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - if (progress != null) { - synchronized (this) { - progress.dismiss(); - progress = null; - } - } - } - } - - private void showToast(final int messageId, final int length) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(activity, messageId, length).show(); - } - }); - } -} diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java b/MyTracks/src/com/google/android/apps/mytracks/TrackList.java index 83bc2af0e..be0af96b1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackList.java @@ -28,10 +28,12 @@ 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.ContentUris; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; @@ -64,7 +66,8 @@ public class TrackList extends ListActivity implements SharedPreferences.OnSharedPreferenceChangeListener, View.OnClickListener { - private static final int DIALOG_INSTALL_EARTH = 0; + private static final int DIALOG_INSTALL_EARTH_ID = 0; + private static final int DIALOG_EXPORT_ALL_ID = 1; private int contextPosition = -1; private long trackId = -1; @@ -180,7 +183,7 @@ public class TrackList extends ListActivity PlayTrackUtils.playTrack(this, trackId); return true; } else { - showDialog(DIALOG_INSTALL_EARTH); + showDialog(DIALOG_INSTALL_EARTH_ID); return true; } case Constants.MENU_SEND_TO_GOOGLE: @@ -257,7 +260,7 @@ public class TrackList extends ListActivity break; } case R.id.tracklist_btn_export_all: { - new ExportAllTracks(this); + showDialog(DIALOG_EXPORT_ALL_ID); break; } case R.id.tracklist_btn_import_all: { @@ -327,8 +330,30 @@ public class TrackList extends ListActivity @Override protected Dialog onCreateDialog(int id) { switch (id) { - case DIALOG_INSTALL_EARTH: + 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, ExportAllActivity.class); + intent.putExtra(ExportAllActivity.EXTRA_TRACK_FILE_FORMAT, + (Parcelable) TrackFileFormat.values()[index]); + TrackList.this.startActivity(intent); + } + }) + .setSingleChoiceItems(choices, 0, null) + .setTitle(R.string.track_list_export_all) + .create(); default: return null; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/SaveActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/SaveActivity.java index 13aae9a44..8ecbfb596 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/SaveActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/SaveActivity.java @@ -51,8 +51,8 @@ public class SaveActivity extends Activity { private static final String TAG = SaveActivity.class.getSimpleName(); - private static final int DIALOG_PROGRESS_ID = 1; - private static final int DIALOG_RESULT_ID = 2; + private static final int DIALOG_PROGRESS_ID = 0; + private static final int DIALOG_RESULT_ID = 1; private long trackId; private TrackFileFormat trackFileFormat;