diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index d9edc8c60..6a17ae991 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -235,7 +235,7 @@ limitations under the License. - diff --git a/MyTracks/src/com/google/android/apps/mytracks/ImportActivity.java b/MyTracks/src/com/google/android/apps/mytracks/ImportActivity.java new file mode 100644 index 000000000..8d47e1be3 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/ImportActivity.java @@ -0,0 +1,207 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.android.apps.mytracks; + +import com.google.android.apps.mytracks.content.TracksColumns; +import com.google.android.apps.mytracks.util.FileUtils; +import com.google.android.apps.mytracks.util.UriUtils; +import com.google.android.maps.mytracks.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.ContentUris; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +/** + * An activity to import GPX files from the SD card. Optionally to import one + * GPX file and display it in My Tracks. + * + * @author Rodrigo Damazio + */ +public class ImportActivity extends Activity { + + public static final String EXTRA_IMPORT_ALL = "import_all"; + + private static final String TAG = ImportActivity.class.getSimpleName(); + + private static final int DIALOG_PROGRESS_ID = 0; + private static final int DIALOG_RESULT_ID = 1; + + private ImportAsyncTask importAsyncTask; + private ProgressDialog progressDialog; + + private boolean importAll; + + // path on the SD card to import + private String path; + + // number of succesfully imported files + private int successCount; + + // number of files to import + private int totalCount; + + // last successfully imported track id + private long trackId; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false); + if (importAll) { + path = new FileUtils().buildExternalDirectoryPath("gpx"); + } else { + String action = intent.getAction(); + if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) { + Log.d(TAG, "Invalid action: " + intent); + finish(); + return; + } + + Uri data = intent.getData(); + if (!UriUtils.isFileUri(data)) { + Log.d(TAG, "Invalid data: " + intent); + finish(); + return; + } + path = data.getPath(); + } + + Object retained = getLastNonConfigurationInstance(); + if (retained instanceof ImportAsyncTask) { + importAsyncTask = (ImportAsyncTask) retained; + importAsyncTask.setActivity(this); + } else { + importAsyncTask = new ImportAsyncTask(this, importAll, path); + importAsyncTask.execute(); + } + } + + @Override + public Object onRetainNonConfigurationInstance() { + importAsyncTask.setActivity(null); + return importAsyncTask; + } + + @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) { + importAsyncTask.cancel(true); + finish(); + } + }); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setTitle(R.string.track_list_import_all); + return progressDialog; + case DIALOG_RESULT_ID: + String message; + if (successCount == 0) { + message = getString(R.string.import_no_file, path); + } else { + String totalFiles = getResources() + .getQuantityString(R.plurals.importGpxFiles, totalCount, totalCount); + message = getString(R.string.import_success, successCount, totalFiles, path); + } + return new AlertDialog.Builder(this).setCancelable(true) + .setCancelable(true) + .setMessage(message) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + finish(); + } + }) + .setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (!importAll && trackId != -1L) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setDataAndType( + ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId), + TracksColumns.CONTENT_ITEMTYPE); + startActivity(intent); + } + finish(); + } + }) + .create(); + default: + return null; + } + } + + /** + * Invokes when the associated AsyncTask completes. + * + * @param success true if the AsyncTask is successful + * @param imported the number of files successfully imported + * @param total the total number of files to import + * @param id the last successfully imported track id + */ + public void onAsyncTaskCompleted(boolean success, int imported, int total, long id) { + successCount = imported; + totalCount = total; + trackId = id; + removeDialog(DIALOG_PROGRESS_ID); + if (success) { + showDialog(DIALOG_RESULT_ID); + } else { + Toast.makeText(this, getString(R.string.import_error, path), 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 files imported + * @param max the maximum number of files + */ + 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/ImportAllTracks.java b/MyTracks/src/com/google/android/apps/mytracks/ImportAllTracks.java deleted file mode 100644 index db329f7e7..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/ImportAllTracks.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.android.apps.mytracks; - -import com.google.android.apps.mytracks.content.MyTracksProviderUtils; -import com.google.android.apps.mytracks.content.TracksColumns; -import com.google.android.apps.mytracks.io.file.GpxImporter; -import com.google.android.apps.mytracks.util.FileUtils; -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.ContentUris; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.os.PowerManager.WakeLock; -import android.util.Log; -import android.widget.Toast; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.xml.parsers.ParserConfigurationException; - -import org.xml.sax.SAXException; - -/** - * A class that will import all GPX tracks in /sdcard/MyTracks/gpx/ - * - * @author David Piggott - */ -public class ImportAllTracks { - - private final Activity activity; - private FileUtils fileUtils; - private boolean singleTrackSelected; - private String gpxPath; - private WakeLock wakeLock; - private ProgressDialog progress; - private int gpxFileCount; - private int importSuccessCount; - private long importedTrackIds[]; - - public ImportAllTracks(Activity activity) { - this(activity, null); - } - - /** - * Constructor to import tracks. - * - * @param activity the activity - * @param path path of the gpx file to import and display. If null, then just - * import all the gpx files under MyTracks/gpx and do not display any - * track. - */ - public ImportAllTracks(Activity activity, String path) { - Log.i(Constants.TAG, "ImportAllTracks: Starting"); - this.activity = activity; - fileUtils = new FileUtils(); - singleTrackSelected = path != null; - gpxPath = path == null ? fileUtils.buildExternalDirectoryPath("gpx") : path; - new Thread(runner).start(); - } - - private final Runnable runner = new Runnable() { - public void run() { - aquireLocksAndImport(); - } - }; - - /** - * Makes sure that we keep the phone from sleeping. See if there is a current - * track. Acquire a wake lock if there is no current track. - */ - private void aquireLocksAndImport() { - 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 import everything. - importAll(); - - // Release the wake lock if we acquired one. - // TODO check what happens if we started recording after getting this lock. - if (wakeLock != null && wakeLock.isHeld()) { - wakeLock.release(); - Log.i(Constants.TAG, "ImportAllTracks: Releasing wake lock."); - } - - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - showDoneDialog(); - } - }); - } - - private void showDoneDialog() { - Log.i(Constants.TAG, "ImportAllTracks: Done"); - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - if (gpxFileCount == 0) { - builder.setMessage(activity.getString(R.string.import_no_file, gpxPath)); - } else { - String totalFiles = activity.getResources().getQuantityString( - R.plurals.importGpxFiles, gpxFileCount, gpxFileCount); - builder.setMessage( - activity.getString(R.string.import_success, importSuccessCount, totalFiles, gpxPath)); - } - builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (singleTrackSelected) { - long lastTrackId = importedTrackIds[importedTrackIds.length - 1]; - Uri trackUri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, lastTrackId); - - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(trackUri, TracksColumns.CONTENT_ITEMTYPE); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - activity.startActivity(intent); - activity.finish(); - } - } - }); - builder.show(); - } - - private void makeProgressDialog(final int trackCount) { - String importMsg = activity.getString(R.string.track_list_import_all); - progress = new ProgressDialog(activity); - progress.setIcon(android.R.drawable.ic_dialog_info); - progress.setTitle(importMsg); - progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - progress.setMax(trackCount); - progress.setProgress(0); - progress.show(); - } - - /** - * Actually import the tracks. This should be called after the wake locks have - * been acquired. - */ - private void importAll() { - MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory.get(activity); - - if (!fileUtils.isSdCardAvailable()) { - return; - } - - List gpxFiles = getGpxFiles(); - gpxFileCount = gpxFiles.size(); - if (gpxFileCount == 0) { - return; - } - - Log.i(Constants.TAG, "ImportAllTracks: Importing: " + gpxFileCount + " tracks."); - activity.runOnUiThread(new Runnable() { - public void run() { - makeProgressDialog(gpxFileCount); - } - }); - - Iterator gpxFilesIterator = gpxFiles.iterator(); - for (int currentFileNumber = 0; gpxFilesIterator.hasNext(); currentFileNumber++) { - File currentFile = gpxFilesIterator.next(); - final int status = currentFileNumber; - activity.runOnUiThread(new Runnable() { - public void run() { - synchronized (this) { - if (progress == null) { - return; - } - progress.setProgress(status); - } - } - }); - if (importFile(currentFile, providerUtils)) { - importSuccessCount++; - } - } - - if (progress != null) { - synchronized (this) { - progress.dismiss(); - progress = null; - } - } - } - - /** - * Attempts to import a GPX file. Returns true on success, issues error - * notifications and returns false on failure. - */ - private boolean importFile(final File gpxFile, MyTracksProviderUtils providerUtils) { - Log.i(Constants.TAG, "ImportAllTracks: importing: " + gpxFile.getName()); - try { - importedTrackIds = GpxImporter.importGPXFile(new FileInputStream(gpxFile), providerUtils); - return true; - } catch (FileNotFoundException e) { - Log.w(Constants.TAG, "GPX file wasn't found/went missing: " - + gpxFile.getAbsolutePath(), e); - } catch (ParserConfigurationException e) { - Log.w(Constants.TAG, "Error parsing file: " + gpxFile.getAbsolutePath(), e); - } catch (SAXException e) { - Log.w(Constants.TAG, "Error parsing file: " + gpxFile.getAbsolutePath(), e); - } catch (IOException e) { - Log.w(Constants.TAG, "Error reading file: " + gpxFile.getAbsolutePath(), e); - } - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - Toast.makeText(activity, activity.getString(R.string.import_error, gpxFile.getName()), - Toast.LENGTH_LONG).show(); - } - }); - return false; - } - - /** - * Gets a list of the GPX files. If singleTrackSelected is true, returns a - * list containing just the gpxPath file. If singleTrackSelected is false, - * returns a list of GPX files under the gpxPath directory. - */ - private List getGpxFiles() { - List gpxFiles = new LinkedList(); - File file = new File(gpxPath); - if (singleTrackSelected) { - gpxFiles.add(file); - } else { - File[] gpxFileCandidates = file.listFiles(); - if (gpxFileCandidates != null) { - for (File candidate : gpxFileCandidates) { - if (!candidate.isDirectory() && candidate.getName().endsWith(".gpx")) { - gpxFiles.add(candidate); - } - } - } - } - return gpxFiles; - } -} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/ImportAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/ImportAsyncTask.java new file mode 100644 index 000000000..4e8898936 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/ImportAsyncTask.java @@ -0,0 +1,221 @@ +/* + * 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.io.file.GpxImporter; +import com.google.android.apps.mytracks.util.FileUtils; +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.os.AsyncTask; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; + +/** + * AsyncTask to import GPX files from the SD card. + * + * @author Jimmy Shih + */ +public class ImportAsyncTask extends AsyncTask { + + private static final String TAG = ImportAsyncTask.class.getSimpleName(); + + private ImportActivity importActivity; + private final boolean importAll; + private final String path; + private final MyTracksProviderUtils myTracksProviderUtils; + private WakeLock wakeLock; + + // true if the AsyncTask result is success + private boolean success; + + // true if the AsyncTask has completed + private boolean completed; + + // number of files successfully imported + private int successCount; + + // number of files to import + private int totalCount; + + // the last successfully imported track id + private long trackId; + + /** + * Creates an AsyncTask. + * + * @param importActivity the activity currently associated with this AsyncTask + * @param importAll true to import all GPX files + * @param path path to import GPX files + */ + public ImportAsyncTask(ImportActivity importActivity, boolean importAll, String path) { + this.importActivity = importActivity; + this.importAll = importAll; + this.path = path; + + myTracksProviderUtils = MyTracksProviderUtils.Factory.get(importActivity); + SharedPreferences sharedPreferences = importActivity.getSharedPreferences( + Constants.SETTINGS_NAME, Context.MODE_PRIVATE); + long recordingTrackId = sharedPreferences.getLong( + importActivity.getString(R.string.recording_track_key), -1L); + + // Get the wake lock if not recording + if (recordingTrackId == -1L) { + wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock); + } + + success = false; + completed = false; + successCount = 0; + totalCount = 0; + trackId = -1L; + } + + /** + * Sets the current {@link ImportActivity} associated with this AyncTask. + * + * @param importActivity the current {@link ImportActivity}, can be null + */ + public void setActivity(ImportActivity importActivity) { + this.importActivity = importActivity; + if (completed && importActivity != null) { + importActivity.onAsyncTaskCompleted(success, successCount, totalCount, trackId); + } + } + + @Override + protected void onPreExecute() { + if (importActivity != null) { + importActivity.showProgressDialog(); + } + } + + @Override + protected Boolean doInBackground(Void... params) { + try { + FileUtils fileUtils = new FileUtils(); + if (!fileUtils.isSdCardAvailable()) { + return false; + } + + List files = getFiles(); + totalCount = files.size(); + if (totalCount == 0) { + return true; + } + + for (int i = 0; i < totalCount; i++) { + if (isCancelled()) { + // If cancelled, return true to show the number of files imported + return true; + } + File file = files.get(i); + if (importFile(file)) { + successCount++; + } + publishProgress(i + 1, totalCount); + } + return true; + } finally { + // Release the wake lock if obtained + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(); + } + } + } + + @Override + protected void onProgressUpdate(Integer... values) { + if (importActivity != null) { + importActivity.setProgressDialogValue(values[0], values[1]); + } + } + + @Override + protected void onPostExecute(Boolean result) { + success = result; + completed = true; + if (importActivity != null) { + importActivity.onAsyncTaskCompleted(success, successCount, totalCount, trackId); + } + } + + /** + * Imports a GPX file. + * + * @param file the file + */ + private boolean importFile(final File file) { + try { + long trackIds[] = GpxImporter.importGPXFile(new FileInputStream(file), myTracksProviderUtils); + int length = trackIds.length; + if (length > 0) { + trackId = trackIds[length - 1]; + } + return true; + } catch (FileNotFoundException e) { + Log.d(TAG, "file: " + file.getAbsolutePath(), e); + return false; + } catch (ParserConfigurationException e) { + Log.d(TAG, "file: " + file.getAbsolutePath(), e); + return false; + } catch (SAXException e) { + Log.d(TAG, "file: " + file.getAbsolutePath(), e); + return false; + } catch (IOException e) { + Log.d(TAG, "file: " + file.getAbsolutePath(), e); + return false; + } + } + + /** + * Gets a list of GPX files. If importAll is true, returns a list of GPX files + * under the path directory. If importAll is false, returns a list containing + * just the path file. + */ + private List getFiles() { + List files = new ArrayList(); + File file = new File(path); + if (importAll) { + File[] candidates = file.listFiles(); + if (candidates != null) { + for (File candidate : candidates) { + if (!candidate.isDirectory() && candidate.getName().endsWith(".gpx")) { + files.add(candidate); + } + } + } + } else { + files.add(file); + } + return files; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java b/MyTracks/src/com/google/android/apps/mytracks/TrackList.java index 66008d043..721cebce8 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackList.java @@ -268,18 +268,17 @@ public class TrackList extends ListActivity @Override public void onClick(View v) { switch (v.getId()) { - case R.id.tracklist_btn_delete_all: { + case R.id.tracklist_btn_delete_all: showDialog(DIALOG_DELETE_ALL_ID); break; - } - case R.id.tracklist_btn_export_all: { + case R.id.tracklist_btn_export_all: showDialog(DIALOG_EXPORT_ALL_ID); break; - } - case R.id.tracklist_btn_import_all: { - new ImportAllTracks(this); - break; - } + case R.id.tracklist_btn_import_all: + Intent intent = new Intent(this, ImportActivity.class) + .putExtra(ImportActivity.EXTRA_IMPORT_ALL, true); + startActivity(intent); + break; } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportActivity.java deleted file mode 100644 index 027e35002..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportActivity.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.android.apps.mytracks.io.file; - -import static com.google.android.apps.mytracks.Constants.TAG; - -import com.google.android.apps.mytracks.ImportAllTracks; -import com.google.android.apps.mytracks.util.UriUtils; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.util.Log; - -/** - * An activity that imports a track from a file and displays the track in My Tracks. - * - * @author Rodrigo Damazio - */ -public class ImportActivity extends Activity { - - @Override - public void onStart() { - super.onStart(); - - Intent intent = getIntent(); - String action = intent.getAction(); - Uri data = intent.getData(); - if (!(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_ATTACH_DATA.equals(action))) { - Log.e(TAG, "Received an intent with unsupported action: " + intent); - finish(); - return; - } - - if (!UriUtils.isFileUri(data)) { - Log.e(TAG, "Received an intent with unsupported data: " + intent); - finish(); - return; - } - - String path = data.getPath(); - Log.i(TAG, "Importing GPX file at " + path); - new ImportAllTracks(this, path); - } -}