sync from main

This commit is contained in:
Jimmy Shih
2012-03-20 11:14:35 -07:00
parent 7318884583
commit 21ba44d422
34 changed files with 1874 additions and 1734 deletions
+17 -24
View File
@@ -158,17 +158,26 @@ limitations under the License.
<meta-data
android:name="android.app.default_searchable"
android:value="com.google.android.apps.mytracks.SearchActivity" />
</activity>
</activity>
<activity android:name="com.google.android.apps.mytracks.ExportAllActivity"/>
<activity
android:name="com.google.android.apps.mytracks.DeleteTrack"
android:label="@string/my_tracks_importer"
android:name="com.google.android.apps.mytracks.ImportActivity"
android:noHistory="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.DELETE" />
<action android:name="android.intent.action.ATTACH_DATA" />
<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="application/gpx" />
<data android:mimeType="application/gpx+xml" />
<data android:mimeType="application/vnd.google-earth.gpx" />
<data android:mimeType="application/vnd.google-earth.gpx+xml" />
<data android:scheme="file" />
</intent-filter>
</activity>
<activity
@@ -274,26 +283,10 @@ limitations under the License.
android:exported="false"
android:label="@string/welcome_title"
android:name="com.google.android.apps.mytracks.WelcomeActivity" />
<activity android:name="com.google.android.apps.mytracks.io.backup.BackupActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.backup.RestoreActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.backup.RestoreChooserActivity"/>
<activity android:name="com.google.android.apps.mytracks.io.docs.SendDocsActivity" />
<activity
android:label="@string/my_tracks_importer"
android:name="com.google.android.apps.mytracks.io.file.ImportActivity"
android:noHistory="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.ATTACH_DATA" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/gpx" />
<data android:mimeType="application/gpx+xml" />
<data android:mimeType="application/vnd.google-earth.gpx" />
<data android:mimeType="application/vnd.google-earth.gpx+xml" />
<data android:scheme="file" />
</intent-filter>
</activity>
<activity
android:name="com.google.android.apps.mytracks.io.file.SaveActivity"
android:noHistory="true"
@@ -312,7 +305,7 @@ limitations under the License.
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.AccountChooserActivity" />
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity" />
<activity android:name="com.google.android.apps.mytracks.io.sendtogoogle.UploadServiceChooserActivity" />
<service
android:exported="false"
android:name="com.google.android.apps.mytracks.services.ControlRecordingService" />
+1 -2
View File
@@ -1537,8 +1537,7 @@ limitations under the License.
and it will overwrite all the current tracks and settings.
Screenshot: settings_backup_restore_confirm_message.png">
Restoring a backup from the SD card will overwrite all your current tracks and settings. Are you
sure?
Restoring a backup from the SD card will overwrite all your current tracks and settings.
</string>
<string name="settings_backup_restore_no_backup"
translation_description="In the 'Backup' settings, after pressing the 'Restore now' option, the
@@ -1,83 +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 com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
/**
* Checks with the user if he prefers the metric units or the imperial units.
*
* @author Sandor Dornbush
*/
class CheckUnits {
private static final String CHECK_UNITS_PREFERENCE_FILE = "checkunits";
private static final String CHECK_UNITS_PREFERENCE_KEY = "checkunits.checked";
private static boolean metric = true;
public static void check(final Context context) {
final SharedPreferences checkUnitsSharedPreferences = context.getSharedPreferences(
CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
if (checkUnitsSharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false)) {
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.preferred_units_title));
CharSequence[] items = { context.getString(R.string.preferred_units_metric),
context.getString(R.string.preferred_units_imperial) };
builder.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
metric = true;
} else {
metric = false;
}
}
});
builder.setCancelable(true);
builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
recordCheckPerformed(checkUnitsSharedPreferences);
SharedPreferences useMetricPreferences = context.getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = useMetricPreferences.edit();
String key = context.getString(R.string.metric_units_key);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor.putBoolean(key, metric));
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
recordCheckPerformed(checkUnitsSharedPreferences);
}
});
builder.show();
}
private static void recordCheckPerformed(SharedPreferences preferences) {
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(
preferences.edit().putBoolean(CHECK_UNITS_PREFERENCE_KEY, true));
}
private CheckUnits() {}
}
@@ -37,16 +37,8 @@ public abstract class Constants {
* onActivityResult request codes:
*/
public static final int SHOW_TRACK = 0;
public static final int SHARE_GPX_FILE = 1;
public static final int SHARE_KML_FILE = 2;
public static final int SHARE_CSV_FILE = 3;
public static final int SHARE_TCX_FILE = 4;
public static final int SAVE_GPX_FILE = 5;
public static final int SAVE_KML_FILE = 6;
public static final int SAVE_CSV_FILE = 7;
public static final int SAVE_TCX_FILE = 8;
public static final int SHOW_WAYPOINT = 9;
public static final int WELCOME = 10;
public static final int SHOW_WAYPOINT = 1;
public static final int WELCOME = 2;
/*
* Context menu ids. Sorted alphabetically.
@@ -168,29 +160,6 @@ public abstract class Constants {
public static final String RESUME_TRACK_EXTRA_NAME =
"com.google.android.apps.mytracks.RESUME_TRACK";
public static int getActionFromMenuId(int menuId) {
switch (menuId) {
case Constants.MENU_SHARE_KML_FILE:
return Constants.SHARE_KML_FILE;
case Constants.MENU_SHARE_GPX_FILE:
return Constants.SHARE_GPX_FILE;
case Constants.MENU_SHARE_CSV_FILE:
return Constants.SHARE_CSV_FILE;
case Constants.MENU_SHARE_TCX_FILE:
return Constants.SHARE_TCX_FILE;
case Constants.MENU_SAVE_GPX_FILE:
return Constants.SAVE_GPX_FILE;
case Constants.MENU_SAVE_KML_FILE:
return Constants.SAVE_KML_FILE;
case Constants.MENU_SAVE_CSV_FILE:
return Constants.SAVE_CSV_FILE;
case Constants.MENU_SAVE_TCX_FILE:
return Constants.SAVE_TCX_FILE;
default:
return -1;
}
}
public static final String MAPSHOP_BASE_URL =
"https://maps.google.com/maps/ms";
@@ -1,85 +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 com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* A utility class that can be used to delete all tracks and track points
* from the provider, including asking for confirmation from the user via
* a dialog.
*
* @author Leif Hendrik Wilden
*/
public class DeleteAllTracks extends Handler {
private final Context context;
private final Runnable done;
public DeleteAllTracks(Context context, Runnable done) {
this.context = context;
this.done = done;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
AlertDialog dialog = null;
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage(
context.getString(R.string.track_list_delete_all_confirm_message));
builder.setTitle(context.getString(R.string.generic_confirm_title));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(context.getString(R.string.generic_yes),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
Log.w(Constants.TAG, "deleting all!");
MyTracksProviderUtils.Factory.get(context).deleteAllTracks();
SharedPreferences prefs = context.getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
Editor editor = prefs.edit();
// TODO: Go through data manager
editor.putLong(context.getString(R.string.selected_track_key), -1);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
if (done != null) {
Handler h = new Handler();
h.post(done);
}
}
});
builder.setNegativeButton(context.getString(R.string.generic_no),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
dialog = builder.create();
dialog.show();
}
}
@@ -1,116 +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 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.util.ApiAdapterFactory;
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.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
/**
* Activity used to delete a track.
*
* @author Rodrigo Damazio
*/
public class DeleteTrack extends Activity
implements DialogInterface.OnClickListener, OnCancelListener {
private static final int CONFIRM_DIALOG = 1;
private MyTracksProviderUtils providerUtils;
private long deleteTrackId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
providerUtils = MyTracksProviderUtils.Factory.get(this);
Intent intent = getIntent();
String action = intent.getAction();
Uri data = intent.getData();
if (!Intent.ACTION_DELETE.equals(action) ||
!UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
Log.e(TAG, "Got bad delete intent: " + intent);
finish();
}
deleteTrackId = ContentUris.parseId(data);
showDialog(CONFIRM_DIALOG);
}
@Override
protected Dialog onCreateDialog(int id) {
if (id != CONFIRM_DIALOG) {
Log.e(TAG, "Unknown dialog " + id);
return null;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getString(R.string.track_list_delete_track_confirm_message));
builder.setTitle(getString(R.string.generic_confirm_title));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(getString(R.string.generic_yes), this);
builder.setNegativeButton(getString(R.string.generic_no), this);
builder.setOnCancelListener(this);
return builder.create();
}
@Override
public void onClick(DialogInterface dialogInterface, int which) {
dialogInterface.dismiss();
if (which == DialogInterface.BUTTON_POSITIVE) {
deleteTrack();
}
finish();
}
@Override
public void onCancel(DialogInterface dialog) {
onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
}
private void deleteTrack() {
providerUtils.deleteTrack(deleteTrackId);
// If the track we just deleted was selected, unselect it.
String selectedKey = getString(R.string.selected_track_key);
SharedPreferences preferences = getSharedPreferences(
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
if (preferences.getLong(selectedKey, -1) == deleteTrackId) {
Editor editor = preferences.edit().putLong(selectedKey, -1);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
}
}
@@ -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));
}
}
}
@@ -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<Void, Integer, Boolean> {
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();
}
}
}
@@ -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();
}
});
}
}
@@ -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));
}
}
}
@@ -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<File> 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<File> 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<File> getGpxFiles() {
List<File> gpxFiles = new LinkedList<File>();
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;
}
}
@@ -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<Void, Integer, Boolean> {
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<File> 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<File> getFiles() {
List<File> files = new ArrayList<File>();
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;
}
}
@@ -22,13 +22,14 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TrackDataHub;
import com.google.android.apps.mytracks.content.TrackDataHub.ListenerDataType;
import com.google.android.apps.mytracks.content.TrackDataListener;
import com.google.android.apps.mytracks.content.TracksColumns;
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.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.LocationUtils;
import com.google.android.apps.mytracks.util.PlayTrackUtils;
@@ -38,11 +39,14 @@ import com.google.android.maps.MapView;
import com.google.android.maps.mytracks.R;
import android.app.Dialog;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.util.Log;
@@ -72,7 +76,8 @@ public class MapActivity extends com.google.android.maps.MapActivity
implements View.OnTouchListener, View.OnClickListener,
TrackDataListener {
private static final int DIALOG_INSTALL_EARTH = 0;
private static final int DIALOG_INSTALL_EARTH_ID = 0;
private static final int DIALOG_DELETE_CURRENT_ID = 1;
// Saved instance state keys:
// ---------------------------
@@ -241,8 +246,26 @@ public class MapActivity extends com.google.android.maps.MapActivity
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_INSTALL_EARTH:
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;
}
@@ -508,7 +531,7 @@ public class MapActivity extends com.google.android.maps.MapActivity
PlayTrackUtils.playTrack(this, trackId);
return true;
} else {
showDialog(DIALOG_INSTALL_EARTH);
showDialog(DIALOG_INSTALL_EARTH_ID);
return true;
}
case Constants.MENU_SEND_TO_GOOGLE:
@@ -527,29 +550,56 @@ public class MapActivity extends com.google.android.maps.MapActivity
startActivity(intent);
return true;
case Constants.MENU_SHARE_GPX_FILE:
case Constants.MENU_SHARE_KML_FILE:
case Constants.MENU_SHARE_CSV_FILE:
case Constants.MENU_SHARE_TCX_FILE:
case Constants.MENU_SAVE_GPX_FILE:
case Constants.MENU_SAVE_KML_FILE:
case Constants.MENU_SAVE_CSV_FILE:
case Constants.MENU_SAVE_TCX_FILE:
SaveActivity.handleExportTrackAction(
this, trackId, Constants.getActionFromMenuId(item.getItemId()));
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:
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
intent = new Intent(Intent.ACTION_DELETE, uri);
startActivity(intent);
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.
*/
@@ -70,7 +70,10 @@ import android.widget.Toast;
@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;
/**
@@ -262,31 +265,59 @@ public class MyTracks extends TabActivity implements OnTouchListener {
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_EULA_ID:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.eula_title);
builder.setMessage(EulaUtils.getEulaMessage(this));
builder.setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
EulaUtils.setEulaValue(MyTracks.this);
Intent startIntent = new Intent(MyTracks.this, WelcomeActivity.class);
startActivityForResult(startIntent, Constants.WELCOME);
}
});
builder.setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setCancelable(true);
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
return builder.create();
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);
Intent startIntent = new Intent(MyTracks.this, WelcomeActivity.class);
startActivityForResult(startIntent, 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;
}
@@ -380,7 +411,11 @@ public class MyTracks extends TabActivity implements OnTouchListener {
break;
}
case Constants.WELCOME: {
CheckUnits.check(this);
SharedPreferences sharedPreferences = getSharedPreferences(
CHECK_UNITS_PREFERENCE_FILE, Context.MODE_PRIVATE);
if (!sharedPreferences.getBoolean(CHECK_UNITS_PREFERENCE_KEY, false)) {
showDialog(DIALOG_CHECK_UNITS_ID);
}
break;
}
@@ -17,8 +17,9 @@ package com.google.android.apps.mytracks;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.io.backup.BackupActivityHelper;
import com.google.android.apps.mytracks.io.backup.BackupActivity;
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
import com.google.android.apps.mytracks.io.backup.RestoreChooserActivity;
import com.google.android.apps.mytracks.services.sensors.ant.AntUtils;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.BluetoothDeviceUtils;
@@ -62,8 +63,9 @@ import java.util.Set;
*/
public class SettingsActivity extends PreferenceActivity {
private static final int DIALOG_CONFIRM_RESET = 0;
private static final int DIALOG_CONFIRM_ACCESS = 1;
private static final int DIALOG_CONFIRM_RESET_ID = 0;
private static final int DIALOG_CONFIRM_ACCESS_ID = 1;
private static final int DIALOG_CONFIRM_RESTORE_ID = 2;
// Value when the task frequency is off.
private static final String TASK_FREQUENCY_OFF = "0";
@@ -150,7 +152,7 @@ public class SettingsActivity extends PreferenceActivity {
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
showDialog(DIALOG_CONFIRM_RESET);
showDialog(DIALOG_CONFIRM_RESET_ID);
return true;
}
});
@@ -162,7 +164,7 @@ public class SettingsActivity extends PreferenceActivity {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if ((Boolean) newValue) {
showDialog(DIALOG_CONFIRM_ACCESS);
showDialog(DIALOG_CONFIRM_ACCESS_ID);
return false;
} else {
return true;
@@ -174,7 +176,7 @@ public class SettingsActivity extends PreferenceActivity {
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CONFIRM_RESET:
case DIALOG_CONFIRM_RESET_ID:
return DialogUtils.createConfirmationDialog(
this, R.string.settings_reset_confirm_message, new OnClickListener() {
@Override
@@ -182,7 +184,7 @@ public class SettingsActivity extends PreferenceActivity {
onResetPreferencesConfirmed();
}
});
case DIALOG_CONFIRM_ACCESS:
case DIALOG_CONFIRM_ACCESS_ID:
return DialogUtils.createConfirmationDialog(
this, R.string.settings_sharing_allow_access_confirm_message, new OnClickListener() {
@Override
@@ -192,6 +194,14 @@ public class SettingsActivity extends PreferenceActivity {
pref.setChecked(true);
}
});
case DIALOG_CONFIRM_RESTORE_ID:
return DialogUtils.createConfirmationDialog(
this, R.string.settings_backup_restore_confirm_message, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startActivity(new Intent(SettingsActivity.this, RestoreChooserActivity.class));
}
});
default:
return null;
}
@@ -354,9 +364,7 @@ public class SettingsActivity extends PreferenceActivity {
new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
BackupActivityHelper backupHelper =
new BackupActivityHelper(SettingsActivity.this);
backupHelper.writeBackup();
startActivity(new Intent(SettingsActivity.this, BackupActivity.class));
return true;
}
});
@@ -364,9 +372,7 @@ public class SettingsActivity extends PreferenceActivity {
new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
BackupActivityHelper backupHelper =
new BackupActivityHelper(SettingsActivity.this);
backupHelper.restoreBackup();
showDialog(DIALOG_CONFIRM_RESTORE_ID);
return true;
}
});
@@ -17,26 +17,31 @@ 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.ContentUris;
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.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -62,7 +67,10 @@ 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 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;
@@ -178,7 +186,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:
@@ -197,21 +205,31 @@ public class TrackList extends ListActivity
startActivity(intent);
return true;
case Constants.MENU_SHARE_GPX_FILE:
case Constants.MENU_SHARE_KML_FILE:
case Constants.MENU_SHARE_CSV_FILE:
case Constants.MENU_SHARE_TCX_FILE:
case Constants.MENU_SAVE_GPX_FILE:
case Constants.MENU_SAVE_KML_FILE:
case Constants.MENU_SAVE_CSV_FILE:
case Constants.MENU_SAVE_TCX_FILE:
SaveActivity.handleExportTrackAction(
this, trackId, Constants.getActionFromMenuId(item.getItemId()));
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:
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
intent = new Intent(Intent.ACTION_DELETE)
.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
startActivity(intent);
showDialog(DIALOG_DELETE_CURRENT_ID);
return true;
default:
Log.w(TAG, "Unknown menu item: " + item.getItemId() + "(" + item.getTitle() + ")");
@@ -219,22 +237,34 @@ public class TrackList extends ListActivity
}
}
/**
* 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: {
Handler h = new DeleteAllTracks(this, null);
h.handleMessage(null);
case R.id.tracklist_btn_delete_all:
showDialog(DIALOG_DELETE_ALL_ID);
break;
}
case R.id.tracklist_btn_export_all: {
new ExportAllTracks(this);
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;
}
}
@@ -298,8 +328,60 @@ 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();
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;
}
@@ -24,10 +24,11 @@ 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.TrackRecordingServiceConnection;
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.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
@@ -57,9 +58,10 @@ import android.widget.Toast;
*
* @author Leif Hendrik Wilden
*/
public class WaypointsList extends ListActivity
implements View.OnClickListener {
public class WaypointsList extends ListActivity implements View.OnClickListener {
private static final int DIALOG_DELETE_CURRENT_ID = 0;
private int contextPosition = -1;
private long trackId = -1;
private long selectedWaypointId = -1;
@@ -119,7 +121,7 @@ public class WaypointsList extends ListActivity
return true;
}
case Constants.MENU_DELETE: {
deleteWaypoint(selectedWaypointId);
showDialog(DIALOG_DELETE_CURRENT_ID);
}
}
}
@@ -199,6 +201,24 @@ public class WaypointsList extends ListActivity
super.onDestroy();
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_DELETE_CURRENT_ID:
return DialogUtils.createConfirmationDialog(this,
R.string.marker_list_delete_marker_confirm_message,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
providerUtils.deleteWaypoint(
selectedWaypointId, new DescriptionGeneratorImpl(WaypointsList.this));
}
});
default:
return null;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_only, menu);
@@ -303,34 +323,4 @@ public class WaypointsList extends ListActivity
});
setListAdapter(adapter);
}
/**
* Deletes the way point with the given id.
* Prompts the user if he want to really delete it.
*/
public void deleteWaypoint(final long waypointId) {
AlertDialog dialog = null;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getString(R.string.marker_list_delete_marker_confirm_message));
builder.setTitle(getString(R.string.generic_confirm_title));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(getString(R.string.generic_yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
providerUtils.deleteWaypoint(waypointId,
new DescriptionGeneratorImpl(WaypointsList.this));
}
});
builder.setNegativeButton(getString(R.string.generic_no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
});
dialog = builder.create();
dialog.show();
}
}
@@ -0,0 +1,101 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.backup;
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.os.Bundle;
import android.widget.Toast;
/**
* Activity to backup data to the SD card.
*
* @author Jimmy Shih
*/
public class BackupActivity extends Activity {
private static final int DIALOG_PROGRESS_ID = 0;
private BackupAsyncTask backupAsyncTask;
private ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof BackupAsyncTask) {
backupAsyncTask = (BackupAsyncTask) retained;
backupAsyncTask.setActivity(this);
} else {
backupAsyncTask = new BackupAsyncTask(this);
backupAsyncTask.execute();
}
}
@Override
public Object onRetainNonConfigurationInstance() {
backupAsyncTask.setActivity(null);
return backupAsyncTask;
}
@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.setMessage(getString(R.string.settings_backup_now_progress_message));
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle(R.string.generic_progress_title);
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);
}
}
@@ -1,240 +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.io.backup;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.MyTracks;
import com.google.android.apps.mytracks.util.FileUtils;
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.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
/**
* Helper which shows a UI for writing or restoring a backup,
* and calls the appropriate handler for actually executing those
* operations.
*
* @author Rodrigo Damazio
*/
public class BackupActivityHelper {
private static final Comparator<Date> REVERSE_DATE_ORDER =
new Comparator<Date>() {
@Override
public int compare(Date s1, Date s2) {
return s2.compareTo(s1);
}
};
private final FileUtils fileUtils;
private final ExternalFileBackup backup;
private final Activity activity;
public BackupActivityHelper(Activity activity) {
this.activity = activity;
this.fileUtils = new FileUtils();
this.backup = new ExternalFileBackup(activity, fileUtils);
}
/**
* Writes a full backup to the default file.
* This shows the results to the user.
*/
public void writeBackup() {
if (!fileUtils.isSdCardAvailable()) {
showToast(R.string.sd_card_error_no_storage);
return;
}
if (!backup.isBackupsDirectoryAvailable(true)) {
showToast(R.string.sd_card_error_create_dir);
return;
}
final ProgressDialog progressDialog = ProgressDialog.show(
activity,
activity.getString(R.string.generic_progress_title),
activity.getString(R.string.settings_backup_now_progress_message),
true);
// Do the writing in another thread
new Thread() {
@Override
public void run() {
try {
backup.writeToDefaultFile();
showToast(R.string.sd_card_success_write_file);
} catch (IOException e) {
Log.e(Constants.TAG, "Failed to write backup", e);
showToast(R.string.sd_card_error_write_file);
} finally {
dismissDialog(progressDialog);
}
}
}.start();
}
/**
* Restores a full backup from the SD card.
* The user will be given a choice of which backup to restore as well as a
* confirmation dialog.
*/
public void restoreBackup() {
// Get the list of existing backups
if (!fileUtils.isSdCardAvailable()) {
showToast(R.string.sd_card_error_no_storage);
return;
}
if (!backup.isBackupsDirectoryAvailable(false)) {
showToast(R.string.settings_backup_restore_no_backup);
return;
}
final Date[] backupDates = backup.getAvailableBackups();
if (backupDates == null || backupDates.length == 0) {
showToast(R.string.settings_backup_restore_no_backup);
return;
}
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
// Show a confirmation dialog
Builder confirmationDialogBuilder = new AlertDialog.Builder(activity);
confirmationDialogBuilder.setMessage(R.string.settings_backup_restore_confirm_message);
confirmationDialogBuilder.setCancelable(true);
confirmationDialogBuilder.setPositiveButton(android.R.string.yes,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pickBackupForRestore(backupDates);
}
});
confirmationDialogBuilder.setNegativeButton(android.R.string.no, null);
confirmationDialogBuilder.create().show();
}
/**
* Shows a backup list for the user to pick, then restores it.
*
* @param backupDates the list of available backup files
*/
private void pickBackupForRestore(final Date[] backupDates) {
if (backupDates.length == 1) {
// Only one choice, don't bother showing the list
restoreFromDateAsync(backupDates[0]);
return;
}
// Make a user-visible version of the backup filenames
final String backupDateStrs[] = new String[backupDates.length];
for (int i = 0; i < backupDates.length; i++) {
backupDateStrs[i] = StringUtils.formatDateTime(activity, backupDates[i].getTime());
}
// Show a dialog for the user to pick which backup to restore
Builder dialogBuilder = new AlertDialog.Builder(activity);
dialogBuilder.setCancelable(true);
dialogBuilder.setTitle(R.string.settings_backup_restore_select_title);
dialogBuilder.setItems(backupDateStrs, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// User picked to restore this one
restoreFromDateAsync(backupDates[which]);
}
});
dialogBuilder.create().show();
}
/**
* Shows a progress dialog, then starts restoring the backup asynchronously.
*
* @param date the date
*/
private void restoreFromDateAsync(final Date date) {
// Show a progress dialog
ProgressDialog.show(
activity,
activity.getString(R.string.generic_progress_title),
activity.getString(R.string.settings_backup_restore_progress_message),
true);
// Do the actual importing in another thread (don't block the UI)
new Thread() {
@Override
public void run() {
try {
backup.restoreFromDate(date);
showToast(R.string.sd_card_success_read_file);
} catch (IOException e) {
Log.e(Constants.TAG, "Failed to restore backup", e);
showToast(R.string.sd_card_error_read_file);
} finally {
// Data may have been restored, "reboot" the app to catch it
restartApplication();
}
}
}.start();
}
/**
* Restarts My Tracks completely.
* This forces any modified data to be re-read.
*/
private void restartApplication() {
Intent intent = new Intent(activity, MyTracks.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivity(intent);
}
/**
* Shows a toast with the given contents.
*/
private void showToast(final int resId) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, resId, Toast.LENGTH_LONG).show();
}
});
}
/**
* Safely dismisses the given dialog.
*/
private void dismissDialog(final Dialog dialog) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
});
}
}
@@ -0,0 +1,113 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.backup;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.maps.mytracks.R;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
/**
* AsyncTask to backup data to the SD card.
*
* @author Jimmy Shih
*/
public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = BackupAsyncTask.class.getSimpleName();
private BackupActivity backupActivity;
private final ExternalFileBackup externalFileBackup;
// 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 backupActivity the activity currently associated with this
* AsyncTask
*/
public BackupAsyncTask(BackupActivity backupActivity) {
this.backupActivity = backupActivity;
this.externalFileBackup = new ExternalFileBackup(backupActivity, new FileUtils());
success = false;
completed = false;
messageId = R.string.sd_card_error_write_file;
}
/**
* Sets the current {@link BackupActivity} associated with this AyncTask.
*
* @param activity the current {@link BackupActivity}, can be null
*/
public void setActivity(BackupActivity activity) {
this.backupActivity = activity;
if (completed && backupActivity != null) {
backupActivity.onAsyncTaskCompleted(success, messageId);
}
}
@Override
protected void onPreExecute() {
if (backupActivity != null) {
backupActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
FileUtils fileUtils = new FileUtils();
if (!fileUtils.isSdCardAvailable()) {
messageId = R.string.sd_card_error_no_storage;
return false;
}
if (!externalFileBackup.isBackupsDirectoryAvailable(true)) {
messageId = R.string.sd_card_error_create_dir;
return false;
}
try {
externalFileBackup.writeToDefaultFile();
messageId = R.string.sd_card_success_write_file;
return true;
} catch (IOException e) {
Log.d(TAG, "IO exception", e);
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
completed = true;
if (backupActivity != null) {
backupActivity.onAsyncTaskCompleted(success, messageId);
}
}
}
@@ -0,0 +1,115 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.backup;
import com.google.android.apps.mytracks.MyTracks;
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.util.Log;
import android.widget.Toast;
import java.util.Date;
/**
* An activity to restore data from the SD card.
*
* @author Jimmy Shih
*/
public class RestoreActivity extends Activity {
public static final String EXTRA_DATE = "date";
private static final String TAG = RestoreActivity.class.getSimpleName();
private static final int DIALOG_PROGRESS_ID = 0;
private RestoreAsyncTask restoreAsyncTask;
private ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof RestoreAsyncTask) {
restoreAsyncTask = (RestoreAsyncTask) retained;
restoreAsyncTask.setActivity(this);
} else {
long date = getIntent().getLongExtra(EXTRA_DATE, -1L);
if (date == -1L) {
Log.d(TAG, "Invalid date");
finish();
}
restoreAsyncTask = new RestoreAsyncTask(this, new Date(date));
restoreAsyncTask.execute();
}
}
@Override
public Object onRetainNonConfigurationInstance() {
restoreAsyncTask.setActivity(null);
return restoreAsyncTask;
}
@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.setMessage(getString(R.string.settings_backup_restore_progress_message));
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle(R.string.generic_progress_title);
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();
startActivity(new Intent(this, MyTracks.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
finish();
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
}
}
@@ -0,0 +1,105 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.backup;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.maps.mytracks.R;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
import java.util.Date;
/**
* AsyncTask to restore data from the SD card.
*
* @author Jimmy Shih
*/
public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = RestoreAsyncTask.class.getSimpleName();
private RestoreActivity restoreActivity;
private final Date date;
private final ExternalFileBackup externalFileBackup;
// 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 restoreActivity the activity currently associated with this
* AsyncTask
* @param date the date to retore from
*/
public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
this.restoreActivity = restoreActivity;
this.date = date;
this.externalFileBackup = new ExternalFileBackup(restoreActivity, new FileUtils());
success = false;
completed = false;
messageId = R.string.sd_card_error_read_file;
}
/**
* Sets the current {@link RestoreActivity} associated with this AyncTask.
*
* @param activity the current {@link RestoreActivity}, can be null
*/
public void setActivity(RestoreActivity activity) {
this.restoreActivity = activity;
if (completed && restoreActivity != null) {
restoreActivity.onAsyncTaskCompleted(success, messageId);
}
}
@Override
protected void onPreExecute() {
if (restoreActivity != null) {
restoreActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
try {
externalFileBackup.restoreFromDate(date);
messageId = R.string.sd_card_success_read_file;
return true;
} catch (IOException e) {
Log.d(TAG, "IO exception", e);
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
completed = true;
if (restoreActivity != null) {
restoreActivity.onAsyncTaskCompleted(success, messageId);
}
}
}
@@ -0,0 +1,122 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.backup;
import com.google.android.apps.mytracks.util.FileUtils;
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.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
/**
* An activity to choose a date to restore from.
*
* @author Jimmy Shih
*/
public class RestoreChooserActivity extends Activity {
private static final Comparator<Date> REVERSE_DATE_ORDER = new Comparator<Date>() {
@Override
public int compare(Date s1, Date s2) {
return s2.compareTo(s1);
}
};
private static final int DIALOG_CHOOSER_ID = 0;
private Date[] backupDates;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FileUtils fileUtils = new FileUtils();
ExternalFileBackup externalFileBackup = new ExternalFileBackup(this, fileUtils);
// Get the list of existing backups
if (!fileUtils.isSdCardAvailable()) {
Toast.makeText(this, R.string.sd_card_error_no_storage, Toast.LENGTH_LONG).show();
finish();
return;
}
if (!externalFileBackup.isBackupsDirectoryAvailable(false)) {
Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
finish();
return;
}
backupDates = externalFileBackup.getAvailableBackups();
if (backupDates == null || backupDates.length == 0) {
Toast.makeText(this, R.string.settings_backup_restore_no_backup, Toast.LENGTH_LONG).show();
finish();
return;
}
if (backupDates.length == 1) {
startActivity(new Intent(this, RestoreActivity.class)
.putExtra(RestoreActivity.EXTRA_DATE, backupDates[0].getTime()));
finish();
return;
}
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
showDialog(DIALOG_CHOOSER_ID);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_CHOOSER_ID:
String items[] = new String[backupDates.length];
for (int i = 0; i < backupDates.length; i++) {
items[i] = StringUtils.formatDateTime(this, backupDates[i].getTime());
}
return new AlertDialog.Builder(this)
.setCancelable(true)
.setItems(items, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startActivity(new Intent(RestoreChooserActivity.this, RestoreActivity.class)
.putExtra(RestoreActivity.EXTRA_DATE, backupDates[which].getTime()));
finish();
}
})
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
})
.setTitle(R.string.settings_backup_restore_select_title)
.create();
default:
return 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);
}
}
@@ -13,24 +13,19 @@
* 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.Constants;
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.util.FileUtils;
import com.google.android.apps.mytracks.util.PlayTrackUtils;
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.content.ContentUris;
import android.content.Context;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
@@ -42,208 +37,190 @@ import android.util.Log;
import java.io.File;
/**
* Activity for saving a track to a file (and optionally sending that file).
* Activity for saving a track to the SD card, and optionally share or play the
* track.
*
* @author Rodrigo Damazio
*/
public class SaveActivity extends Activity {
public static final String EXTRA_FILE_FORMAT = "file_format";
public static final String EXTRA_SHARE_FILE = "share_file";
public static final String EXTRA_PLAY_FILE = "play_file";
private static final int RESULT_DIALOG = 1;
/* VisibleForTesting */
static final int PROGRESS_DIALOG = 2;
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
public static final String EXTRA_SHARE_TRACK = "share_track";
public static final String EXTRA_PLAY_TRACK = "play_track";
private static final String TAG = SaveActivity.class.getSimpleName();
private static final int DIALOG_PROGRESS_ID = 0;
private static final int DIALOG_RESULT_ID = 1;
private MyTracksProviderUtils providerUtils;
private long trackId;
private TrackWriter writer;
private boolean shareFile;
private boolean playFile;
private TrackFileFormat format;
private WriteProgressController controller;
private TrackFileFormat trackFileFormat;
private boolean shareTrack;
private boolean playTrack;
private SaveAsyncTask saveAsyncTask;
private ProgressDialog progressDialog;
// result from the AsyncTask
private boolean success;
// message id from the AsyncTask
private int messageId;
// path of the saved file
private String filePath;
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
providerUtils = MyTracksProviderUtils.Factory.get(this);
Intent intent = getIntent();
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
if (trackId < 0) {
Log.d(TAG, "Invalid track id");
finish();
return;
}
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
shareTrack = intent.getBooleanExtra(EXTRA_SHARE_TRACK, false);
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof SaveAsyncTask) {
saveAsyncTask = (SaveAsyncTask) retained;
saveAsyncTask.setActivity(this);
} else {
TrackWriter trackWriter = TrackWriterFactory.newWriter(
this, MyTracksProviderUtils.Factory.get(this), trackId, trackFileFormat);
if (trackWriter == null) {
Log.e(TAG, "Track writer is null");
finish();
return;
}
if (shareTrack || playTrack) {
// Save to the temp directory
String dirName = new FileUtils().buildExternalDirectoryPath(
trackFileFormat.getExtension(), "tmp");
trackWriter.setDirectory(new File(dirName));
}
saveAsyncTask = new SaveAsyncTask(this, trackWriter);
saveAsyncTask.execute();
}
}
@Override
protected void onStart() {
super.onStart();
public Object onRetainNonConfigurationInstance() {
saveAsyncTask.setActivity(null);
return saveAsyncTask;
}
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
Uri data = intent.getData();
if (!getString(R.string.track_action_save).equals(action)
|| !TracksColumns.CONTENT_ITEMTYPE.equals(type)
|| !UriUtils.matchesContentUri(data, TracksColumns.CONTENT_URI)) {
Log.e(TAG, "Got bad save intent: " + intent);
finish();
return;
@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.setMessage(getString(R.string.sd_card_progress_write_file));
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
saveAsyncTask.cancel(true);
finish();
}
});
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle(R.string.generic_progress_title);
return progressDialog;
case DIALOG_RESULT_ID:
return new AlertDialog.Builder(this)
.setCancelable(true)
.setIcon(success
? android.R.drawable.ic_dialog_info : android.R.drawable.ic_dialog_alert)
.setMessage(messageId)
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
onPostResultDialog();
}
})
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
dialog.dismiss();
onPostResultDialog();
}
})
.setTitle(success ? R.string.generic_success_title : R.string.generic_error_title)
.create();
default:
return null;
}
}
trackId = ContentUris.parseId(data);
/**
* Invokes when the associated AsyncTask completes.
*
* @param isSuccess true if the AsyncTask is successful
* @param aMessageId the id of the AsyncTask message
* @param aPath the path of the saved file
*/
public void onAsyncTaskCompleted(boolean isSuccess, int aMessageId, String aPath) {
this.success = isSuccess;
this.messageId = aMessageId;
this.filePath = aPath;
removeDialog(DIALOG_PROGRESS_ID);
showDialog(DIALOG_RESULT_ID);
}
int formatIdx = intent.getIntExtra(EXTRA_FILE_FORMAT, -1);
format = TrackFileFormat.values()[formatIdx];
shareFile = intent.getBooleanExtra(EXTRA_SHARE_FILE, false);
playFile = intent.getBooleanExtra(EXTRA_PLAY_FILE, false);
writer = TrackWriterFactory.newWriter(this, providerUtils, trackId, format);
if (writer == null) {
Log.e(TAG, "Unable to build writer");
finish();
return;
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
}
/**
* Sets the progress dialog value.
*
* @param number the number of points saved
* @param max the maximum number of points
*/
public void setProgressDialogValue(int number, int max) {
if (progressDialog != null) {
progressDialog.setIndeterminate(false);
progressDialog.setMax(max);
progressDialog.setProgress(Math.min(number, max));
}
if (shareFile || playFile) {
// If the file is for sending, save it to a temporary location instead.
FileUtils fileUtils = new FileUtils();
String extension = format.getExtension();
String dirName = fileUtils.buildExternalDirectoryPath(extension, "tmp");
File dir = new File(dirName);
writer.setDirectory(dir);
}
controller = new WriteProgressController(this, writer, PROGRESS_DIALOG);
controller.setOnCompletionListener(new WriteProgressController.OnCompletionListener() {
@Override
public void onComplete() {
showDialog(RESULT_DIALOG);
}
});
controller.startWrite();
}
/**
* To be invoked after showing the result dialog.
*/
private void onPostResultDialog() {
if (writer.wasSuccess()) {
if (shareFile) {
shareWrittenFile();
} else if (playFile) {
playWrittenFile();
if (success) {
if (shareTrack) {
Intent intent = new Intent(Intent.ACTION_SEND)
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filePath)))
.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_track_subject))
.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_track_file_body_format))
.putExtra(getString(R.string.track_id_broadcast_extra), trackId)
.setType(trackFileFormat.getMimeType());
startActivity(Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
} else if (playTrack) {
Intent intent = new Intent()
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(PlayTrackUtils.TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID)
.setClassName(PlayTrackUtils.GOOGLE_EARTH_PACKAGE, PlayTrackUtils.GOOGLE_EARTH_CLASS)
.setDataAndType(Uri.fromFile(new File(filePath)), PlayTrackUtils.KML_MIME_TYPE);
startActivity(intent);
}
}
finish();
}
private void shareWrittenFile() {
// Share the file.
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
getResources().getText(R.string.share_track_subject).toString());
shareIntent.putExtra(Intent.EXTRA_TEXT,
getResources().getText(R.string.share_track_file_body_format)
.toString());
shareIntent.setType(format.getMimeType());
Uri u = Uri.fromFile(new File(writer.getAbsolutePath()));
shareIntent.putExtra(Intent.EXTRA_STREAM, u);
shareIntent.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
startActivity(Intent.createChooser(shareIntent,
getResources().getText(R.string.share_track_picker_title).toString()));
}
private void playWrittenFile() {
Uri uri = Uri.fromFile(new File(writer.getAbsolutePath()));
Intent intent = new Intent()
.setClassName(PlayTrackUtils.GOOGLE_EARTH_PACKAGE, PlayTrackUtils.GOOGLE_EARTH_CLASS)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setDataAndType(uri, PlayTrackUtils.KML_MIME_TYPE)
.putExtra(PlayTrackUtils.TOUR_FEATURE_ID, KmlTrackWriter.TOUR_FEATURE_ID);
startActivity(intent);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case RESULT_DIALOG:
return createResultDialog();
case PROGRESS_DIALOG:
if (controller != null) {
return controller.createProgressDialog();
}
//$FALL-THROUGH$
default:
return super.onCreateDialog(id);
}
}
private Dialog createResultDialog() {
boolean success = writer.wasSuccess();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(writer.getErrorMessage());
builder.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
dialog.dismiss();
onPostResultDialog();
}
});
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
onPostResultDialog();
}
});
builder.setIcon(success ? android.R.drawable.ic_dialog_info :
android.R.drawable.ic_dialog_alert);
builder.setTitle(success ? R.string.generic_success_title : R.string.generic_error_title);
return builder.create();
}
public static void handleExportTrackAction(Context ctx, long trackId, int actionCode) {
if (trackId < 0) {
return;
}
TrackFileFormat exportFormat = null;
switch (actionCode) {
case Constants.SAVE_GPX_FILE:
case Constants.SHARE_GPX_FILE:
exportFormat = TrackFileFormat.GPX;
break;
case Constants.SAVE_KML_FILE:
case Constants.SHARE_KML_FILE:
exportFormat = TrackFileFormat.KML;
break;
case Constants.SAVE_CSV_FILE:
case Constants.SHARE_CSV_FILE:
exportFormat = TrackFileFormat.CSV;
break;
case Constants.SAVE_TCX_FILE:
case Constants.SHARE_TCX_FILE:
exportFormat = TrackFileFormat.TCX;
break;
default:
throw new IllegalArgumentException("Warning unhandled action code: " + actionCode);
}
boolean shareFile = false;
switch (actionCode) {
case Constants.SHARE_GPX_FILE:
case Constants.SHARE_KML_FILE:
case Constants.SHARE_CSV_FILE:
case Constants.SHARE_TCX_FILE:
shareFile = true;
}
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
Intent intent = new Intent(ctx, SaveActivity.class);
intent.setAction(ctx.getString(R.string.track_action_save));
intent.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
intent.putExtra(EXTRA_FILE_FORMAT, exportFormat.ordinal());
intent.putExtra(EXTRA_SHARE_FILE, shareFile);
ctx.startActivity(intent);
}
}
@@ -0,0 +1,107 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.file;
import android.os.AsyncTask;
/**
* Async Task to save a track to the SD card.
*
* @author Jimmy Shih
*/
public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private SaveActivity saveActivity;
private final TrackWriter trackWriter;
// true if the AsyncTask result is success
private boolean success;
// true if the AsyncTask has completed
private boolean completed;
/**
* Creates an AsyncTask.
*
* @param saveActivity the {@link SaveActivity} currently associated with this
* AsyncTask
* @param trackWriter the track writer
*/
public SaveAsyncTask(SaveActivity saveActivity, TrackWriter trackWriter) {
this.saveActivity = saveActivity;
this.trackWriter = trackWriter;
success = false;
completed = false;
}
/**
* Sets the current {@link SaveActivity} associated with this AyncTask.
*
* @param saveActivity the current {@link SaveActivity}, can be null
*/
public void setActivity(SaveActivity saveActivity) {
this.saveActivity = saveActivity;
if (completed && saveActivity != null) {
saveActivity.onAsyncTaskCompleted(
success, trackWriter.getErrorMessage(), trackWriter.getAbsolutePath());
}
}
@Override
protected void onPreExecute() {
if (saveActivity != null) {
saveActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
trackWriter.setOnWriteListener(new TrackWriter.OnWriteListener() {
@Override
public void onWrite(int number, int max) {
// Update the progress dialog once every 500 points
if (number % 500 == 0) {
publishProgress(number, max);
}
}
});
trackWriter.writeTrack();
return trackWriter.wasSuccess();
}
@Override
protected void onProgressUpdate(Integer... values) {
if (saveActivity != null) {
saveActivity.setProgressDialogValue(values[0], values[1]);
}
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
completed = true;
if (saveActivity != null) {
saveActivity.onAsyncTaskCompleted(
success, trackWriter.getErrorMessage(), trackWriter.getAbsolutePath());
}
}
@Override
protected void onCancelled() {
trackWriter.stopWriteTrack();
}
}
@@ -27,10 +27,6 @@ import java.io.File;
* @author Rodrigo Damazio
*/
public interface TrackWriter {
/** This listener is used to signal completion of track write */
public interface OnCompletionListener {
public void onComplete();
}
/** This listener is used to signal track writes. */
public interface OnWriteListener {
@@ -43,11 +39,6 @@ public interface TrackWriter {
public void onWrite(int number, int max);
}
/**
* Sets listener to be invoked when the writer has finished.
*/
void setOnCompletionListener(OnCompletionListener onCompletionListener);
/**
* Sets a listener to be invoked for each location writer.
*/
@@ -63,12 +54,6 @@ public interface TrackWriter {
*/
String getAbsolutePath();
/**
* Writes the given track id to the SD card.
* This is non-blocking.
*/
void writeTrackAsync();
/**
* Writes the given track id to the SD card.
* This is blocking.
@@ -22,6 +22,8 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
@@ -34,7 +36,7 @@ public class TrackWriterFactory {
/**
* Definition of all possible track formats.
*/
public enum TrackFileFormat {
public enum TrackFileFormat implements Parcelable {
GPX {
@Override
TrackFormatWriter newFormatWriter(Context context) {
@@ -60,6 +62,28 @@ public class TrackWriterFactory {
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeInt(ordinal());
}
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
@Override
public TrackFileFormat createFromParcel(final Parcel source) {
return TrackFileFormat.values()[source.readInt()];
}
@Override
public TrackFileFormat[] newArray(final int size) {
return new TrackFileFormat[size];
}
};
/**
* Creates and returns a new format writer for each format.
*/
@@ -57,7 +57,6 @@ class TrackWriterImpl implements TrackWriter {
private int errorMessage = -1;
private File directory = null;
private File file = null;
private OnCompletionListener onCompletionListener;
private OnWriteListener onWriteListener;
private Thread writeThread;
@@ -70,11 +69,6 @@ class TrackWriterImpl implements TrackWriter {
this.fileUtils = new FileUtils();
}
@Override
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
@Override
public void setOnWriteListener(OnWriteListener onWriteListener) {
this.onWriteListener = onWriteListener;
@@ -90,8 +84,7 @@ class TrackWriterImpl implements TrackWriter {
return file.getAbsolutePath();
}
@Override
public void writeTrackAsync() {
private void writeTrackAsync() {
writeThread = new Thread() {
@Override
public void run() {
@@ -131,7 +124,6 @@ class TrackWriterImpl implements TrackWriter {
}
}
}
finished();
}
public void stopWriteTrack() {
@@ -163,18 +155,6 @@ class TrackWriterImpl implements TrackWriter {
* ===============
*/
private void finished() {
if (onCompletionListener != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
onCompletionListener.onComplete();
}
});
return;
}
}
/**
* Runs the given runnable in the UI thread.
*/
@@ -1,132 +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 com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
/**
* Given a {@link TrackWriter}, this class manages the process of writing the
* data in the track represented by the writer. This includes the display of
* a progress dialog, updating the progress bar in said dialog, and notifying
* interested parties when the write completes.
*
* @author Matthew Simmons
*/
class WriteProgressController {
/**
* This listener is used to notify interested parties when the write has
* completed.
*/
public interface OnCompletionListener {
/**
* When this method is invoked, the write has completed, and the progress
* dialog has been dismissed. Whether the write succeeded can be
* determined by examining the {@link TrackWriter}.
*/
public void onComplete();
}
private final Activity activity;
private final TrackWriter writer;
private ProgressDialog dialog;
private OnCompletionListener onCompletionListener;
private final int progressDialogId;
/**
* @param activity the activity associated with this write
* @param writer the writer which writes the track to disk. Note that this
* class will use the writer's completion listener. If callers are
* interested in notification upon completion of the write, they should
* use {@link #setOnCompletionListener}.
*/
public WriteProgressController(Activity activity, TrackWriter writer, int progressDialogId) {
this.activity = activity;
this.writer = writer;
this.progressDialogId = progressDialogId;
writer.setOnCompletionListener(writerCompleteListener);
writer.setOnWriteListener(writerWriteListener);
}
/** Set a listener to be invoked when the write completes. */
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
// For testing purpose
OnCompletionListener getOnCompletionListener() {
return onCompletionListener;
}
public ProgressDialog createProgressDialog() {
dialog = new ProgressDialog(activity);
dialog.setIcon(android.R.drawable.ic_dialog_info);
dialog.setTitle(activity.getString(R.string.generic_progress_title));
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage(activity.getString(R.string.sd_card_progress_write_file));
dialog.setIndeterminate(true);
dialog.setOnCancelListener(dialogCancelListener);
return dialog;
}
/** Initiate an asynchronous write. */
public void startWrite() {
activity.showDialog(progressDialogId);
writer.writeTrackAsync();
}
/** VisibleForTesting */
ProgressDialog getDialog() {
return dialog;
}
private final DialogInterface.OnCancelListener dialogCancelListener =
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
writer.stopWriteTrack();
}
};
private final TrackWriter.OnCompletionListener writerCompleteListener =
new TrackWriter.OnCompletionListener() {
@Override
public void onComplete() {
activity.dismissDialog(progressDialogId);
if (onCompletionListener != null) {
onCompletionListener.onComplete();
}
}
};
private final TrackWriter.OnWriteListener writerWriteListener =
new TrackWriter.OnWriteListener() {
@Override
public void onWrite(int number, int max) {
if (number % 500 == 0) {
dialog.setIndeterminate(false);
dialog.setMax(max);
dialog.setProgress(Math.min(number, max));
}
}
};
}
@@ -16,14 +16,12 @@
package com.google.android.apps.mytracks.util;
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.maps.mytracks.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -31,6 +29,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Parcelable;
import java.util.List;
@@ -80,12 +79,10 @@ public class PlayTrackUtils {
public static void playTrack(Context context, long trackId) {
AnalyticsUtils.sendPageViews(context, "/action/play");
Uri uri = ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId);
Intent intent = new Intent(context, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_FILE_FORMAT, TrackFileFormat.KML.ordinal())
.putExtra(SaveActivity.EXTRA_PLAY_FILE, true)
.setAction(context.getString(R.string.track_action_save))
.setDataAndType(uri, TracksColumns.CONTENT_ITEMTYPE);
.putExtra(SaveActivity.EXTRA_TRACK_ID, trackId)
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) TrackFileFormat.KML)
.putExtra(SaveActivity.EXTRA_PLAY_TRACK, true);
context.startActivity(intent);
}
@@ -19,21 +19,14 @@ import java.io.File;
/**
* A simple, fake {@link TrackWriter} subclass with all methods mocked out.
* Tests are expected to override {@link #writeTrack} and/or
* {@link #writeTrackAsync}, at the very least.
* Tests are expected to override {@link #writeTrack}.
*
* @author Matthew Simmons
*
*/
public class MockTrackWriter implements TrackWriter {
public OnCompletionListener onCompletionListener;
public OnWriteListener onWriteListener;
@Override
public void setOnCompletionListener(OnCompletionListener onCompletionListener) {
this.onCompletionListener = onCompletionListener;
}
@Override
public void setOnWriteListener(OnWriteListener onWriteListener) {
this.onWriteListener = onWriteListener;
@@ -49,11 +42,6 @@ public class MockTrackWriter implements TrackWriter {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void writeTrackAsync() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void writeTrack() {
throw new UnsupportedOperationException("not implemented");
@@ -169,13 +169,7 @@ public class TrackWriterTest extends AndroidTestCase {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, true);
// Expect the completion listener to be run
TrackWriter.OnCompletionListener completionListener
= mocksControl.createMock(TrackWriter.OnCompletionListener.class);
completionListener.onComplete();
mocksControl.replay();
writer.setOnCompletionListener(completionListener);
writer.writeTrack();
assertEquals(1, writeDocumentCalls);
@@ -187,13 +181,7 @@ public class TrackWriterTest extends AndroidTestCase {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, false);
// Expect the completion listener to be run
TrackWriter.OnCompletionListener completionListener
= mocksControl.createMock(TrackWriter.OnCompletionListener.class);
completionListener.onComplete();
mocksControl.replay();
writer.setOnCompletionListener(completionListener);
writer.writeTrack();
assertEquals(0, writeDocumentCalls);
@@ -1,90 +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 android.app.ProgressDialog;
import android.test.ActivityInstrumentationTestCase2;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
/**
* Tests {@link WriteProgressController}.
*
* @author Matthew Simmons
*/
public class WriteProgressControllerTest extends ActivityInstrumentationTestCase2<SaveActivity> {
public WriteProgressControllerTest() {
super(SaveActivity.class);
}
private static void assertProgress(ProgressDialog dialog, int expectedProgress,
int expectedMax) {
assertEquals(expectedProgress, dialog.getProgress());
assertEquals(expectedMax, dialog.getMax());
}
public void testSimple() throws Exception {
final AtomicReference<ProgressDialog> dialogRef = new AtomicReference<ProgressDialog>();
final AtomicReference<Boolean> controllerDoneRef = new AtomicReference<Boolean>();
final Semaphore writerDone = new Semaphore(0);
TrackWriter mockWriter = new MockTrackWriter() {
@Override
public void writeTrackAsync() {
onWriteListener.onWrite(1000, 10000);
assertProgress(dialogRef.get(), 1000, 10000);
onCompletionListener.onComplete();
writerDone.release();
}
};
final WriteProgressController controller = new WriteProgressController(
getActivity(), mockWriter, SaveActivity.PROGRESS_DIALOG);
controller.setOnCompletionListener(new WriteProgressController.OnCompletionListener() {
@Override
public void onComplete() {
controllerDoneRef.set(true);
}
});
/*
* The WriteProgressController constructor calls the mockWriter's
* setOnCompletionListener method with a listener that dismisses the
* progress dialog. However, this unit test only tests the
* WriteProgressController and doesn't actually show any progress dialog.
* Thus after the WriteProgressController is setup, we need to call the
* mockWriter's setOnCompletionListener method again with an listener that
* doesn't dismiss dialog.
*/
mockWriter.setOnCompletionListener(new TrackWriter.OnCompletionListener() {
@Override
public void onComplete() {
controller.getOnCompletionListener().onComplete();
}
});
dialogRef.set(controller.createProgressDialog());
controller.startWrite();
// wait for the writer to finish
writerDone.acquire();
assertTrue(controllerDoneRef.get());
}
}