merge heads

This commit is contained in:
Jimmy Shih
2012-02-01 13:44:50 -08:00
19 changed files with 403 additions and 578 deletions
@@ -22,7 +22,7 @@ import android.app.Application;
/**
* MyTracksApplication for keeping global state.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class MyTracksApplication extends Application {
@@ -15,107 +15,33 @@
*/
package com.google.android.apps.mytracks.io.docs;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
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;
/**
* An activity to send a track to Google Docs.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendDocsActivity extends Activity {
private static final int PROGRESS_DIALOG = 1;
private SendRequest sendRequest;
private SendDocsAsyncTask asyncTask;
private ProgressDialog progressDialog;
public class SendDocsActivity extends AbstractSendActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof SendDocsAsyncTask) {
asyncTask = (SendDocsAsyncTask) retained;
asyncTask.setActivity(this);
} else {
asyncTask = new SendDocsAsyncTask(this, sendRequest.getTrackId(), sendRequest.getAccount());
asyncTask.execute();
}
protected AbstractSendAsyncTask createAsyncTask() {
return new SendDocsAsyncTask(this, sendRequest.getTrackId(), sendRequest.getAccount());
}
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.setActivity(null);
return asyncTask;
protected String getServiceName() {
return getString(R.string.send_google_docs);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(getString(
R.string.send_google_progress_title, getString(R.string.send_google_docs)));
progressDialog.setMax(100);
progressDialog.setProgress(0);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
startNextActivity(false);
}
});
return progressDialog;
default:
return null;
}
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param success true if success
*/
public void onAsyncTaskCompleted(boolean success) {
startNextActivity(success);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(PROGRESS_DIALOG);
}
/**
* Sets the progress dialog value.
*/
public void setProgressDialogValue(int value) {
if (progressDialog != null) {
progressDialog.setProgress(value);
}
}
/**
* Starts the next activity.
*
* @param success true if sendDocs is success
*/
private void startNextActivity(boolean success) {
protected void startNextActivity(boolean success, boolean isCancel) {
sendRequest.setDocsSuccess(success);
Intent intent = new Intent(this, UploadResultActivity.class)
.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
@@ -21,6 +21,7 @@ import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
import com.google.android.apps.mytracks.io.gdata.docs.DocumentsClient;
import com.google.android.apps.mytracks.io.gdata.docs.SpreadsheetsClient;
import com.google.android.apps.mytracks.io.gdata.docs.XmlDocsGDataParserFactory;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.common.gdata.AndroidXmlParserFactory;
import com.google.android.maps.mytracks.R;
import com.google.wireless.gdata.client.GDataClient;
@@ -33,7 +34,6 @@ import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
@@ -41,9 +41,9 @@ import java.io.IOException;
/**
* AsyncTask to send a track to Google Docs.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
public class SendDocsAsyncTask extends AbstractSendAsyncTask {
private static final int PROGRESS_GET_SPREADSHEET_ID = 0;
private static final int PROGRESS_CREATE_SPREADSHEET = 25;
private static final int PROGRESS_GET_WORKSHEET_ID = 50;
@@ -52,7 +52,6 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = SendDocsAsyncTask.class.getSimpleName();
private SendDocsActivity activity;
private final long trackId;
private final Account account;
private final Context context;
@@ -61,21 +60,6 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private final DocumentsClient documentsClient;
private final SpreadsheetsClient spreadsheetsClient;
/**
* True if can retry sending to Google Docs.
*/
private boolean canRetry;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if the result is success.
*/
private boolean success;
// The following variables are for per upload states
private String documentsAuthToken;
private String spreadsheetsAuthToken;
@@ -83,7 +67,7 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private String worksheetId;
public SendDocsAsyncTask(SendDocsActivity activity, long trackId, Account account) {
this.activity = activity;
super(activity);
this.trackId = trackId;
this.account = account;
@@ -94,68 +78,22 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
gDataClient, new XmlDocsGDataParserFactory(new AndroidXmlParserFactory()));
spreadsheetsClient = new SpreadsheetsClient(
gDataClient, new XmlDocsGDataParserFactory(new AndroidXmlParserFactory()));
canRetry = true;
completed = false;
success = false;
}
/**
* Sets the activity associated with this AyncTask.
*
* @param activity the activity.
*/
public void setActivity(SendDocsActivity activity) {
this.activity = activity;
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onPreExecute() {
activity.showProgressDialog();
}
@Override
protected Boolean doInBackground(Void... params) {
return doUpload();
}
@Override
protected void onProgressUpdate(Integer... values) {
if (activity != null) {
activity.setProgressDialogValue(values[0]);
}
}
@Override
protected void onPostExecute(Boolean result) {
closeClient();
success = result;
completed = true;
if (activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onCancelled() {
closeClient();
}
private void closeClient() {
protected void closeConnection() {
if (gDataClient != null) {
gDataClient.close();
}
}
/**
* Uploads a track to Google Docs.
*
* @return true if success.
*/
private boolean doUpload() {
@Override
protected void saveResult() {
// No action for Google Docs
}
@Override
protected boolean performTask() {
// Reset the per upload states
documentsAuthToken = null;
spreadsheetsAuthToken = null;
@@ -169,13 +107,13 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
account, spreadsheetsClient.getServiceName(), false);
} catch (OperationCanceledException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (AuthenticatorException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (IOException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
}
Track track = myTracksProviderUtils.getTrack(trackId);
@@ -192,7 +130,7 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
// Get the spreadsheet ID
publishProgress(PROGRESS_GET_SPREADSHEET_ID);
if (!fetchSpreadSheetId(title, false)) {
return retryUpload();
return retryTask();
}
// Create a new spreadsheet if necessary
@@ -223,7 +161,7 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
// Get the worksheet ID
publishProgress(PROGRESS_GET_WORKSHEET_ID);
if (!fetchWorksheetId()) {
return retryUpload();
return retryTask();
}
if (worksheetId == null) {
Log.d(TAG, "Unable to get a worksheet ID");
@@ -241,6 +179,14 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
return true;
}
@Override
protected void invalidateToken() {
AccountManager.get(context).invalidateAuthToken(
documentsClient.getServiceName(), documentsAuthToken);
AccountManager.get(context).invalidateAuthToken(
spreadsheetsClient.getServiceName(), spreadsheetsAuthToken);
}
/**
* Fetches the spreadsheet id. Sets the instance variable
* {@link SendDocsAsyncTask#spreadsheetId}.
@@ -366,24 +312,4 @@ public class SendDocsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
}
return true;
}
/**
* Retries upload. Invalidates the authToken. If can retry, invokes
* {@link SendDocsAsyncTask#doUpload()}. Returns false if cannot retry.
*/
private boolean retryUpload() {
if (isCancelled()) {
return false;
}
AccountManager.get(context).invalidateAuthToken(
documentsClient.getServiceName(), documentsAuthToken);
AccountManager.get(context).invalidateAuthToken(
spreadsheetsClient.getServiceName(), spreadsheetsAuthToken);
if (canRetry) {
canRetry = false;
return doUpload();
}
return false;
}
}
@@ -16,121 +16,35 @@
package com.google.android.apps.mytracks.io.fusiontables;
import com.google.android.apps.mytracks.io.docs.SendDocsActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
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;
/**
* An activity to send a track to Google Fusion Tables.
* <p>
* The activity gets recreated when the screen rotates. To support the activity
* displaying a progress dialog, we do the following:
* <ul>
* <li>use one instance of an AyncTask to send the track</li>
* <li>save that instance as the last non configuration instance of the activity
* </li>
* <li>when a new activity is created, pass the activity to the AsyncTask so
* that the AsyncTask can update the progress dialog of the activity</li>
* </ul>
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendFusionTablesActivity extends Activity {
private static final int PROGRESS_DIALOG = 1;
private SendRequest sendRequest;
private SendFusionTablesAsyncTask asyncTask;
private ProgressDialog progressDialog;
public class SendFusionTablesActivity extends AbstractSendActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof SendFusionTablesAsyncTask) {
asyncTask = (SendFusionTablesAsyncTask) retained;
asyncTask.setActivity(this);
} else {
asyncTask = new SendFusionTablesAsyncTask(
this, sendRequest.getTrackId(), sendRequest.getAccount());
asyncTask.execute();
}
protected AbstractSendAsyncTask createAsyncTask() {
return new SendFusionTablesAsyncTask(this, sendRequest.getTrackId(), sendRequest.getAccount());
}
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.setActivity(null);
return asyncTask;
protected String getServiceName() {
return getString(R.string.send_google_fusion_tables);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(getString(
R.string.send_google_progress_title, getString(R.string.send_google_fusion_tables)));
progressDialog.setMax(100);
progressDialog.setProgress(0);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
startNextActivity(false, true);
}
});
return progressDialog;
default:
return null;
}
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param success true if success
*/
public void onAsyncTaskCompleted(boolean success) {
startNextActivity(success, false);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(PROGRESS_DIALOG);
}
/**
* Sets the progress dialog value.
*/
public void setProgressDialogValue(int value) {
if (progressDialog != null) {
progressDialog.setProgress(value);
}
}
/**
* Starts the next activity.
*
* @param success true if sendFusionTables is success
* @param isCancel true if it is a cancel request
*/
private void startNextActivity(boolean success, boolean isCancel) {
protected void startNextActivity(boolean success, boolean isCancel) {
sendRequest.setFusionTablesSuccess(success);
Class<?> next;
if (isCancel) {
next = UploadResultActivity.class;
@@ -141,8 +55,7 @@ public class SendFusionTablesActivity extends Activity {
next = UploadResultActivity.class;
}
}
Intent intent = new Intent(this, next)
.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
Intent intent = new Intent(this, next).putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
startActivity(intent);
finish();
}
@@ -6,6 +6,7 @@ import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
@@ -32,7 +33,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;
import java.io.ByteArrayInputStream;
@@ -46,9 +46,9 @@ import java.util.Vector;
/**
* AsyncTask to send a track to Google Fusion Tables.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean> {
public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
private static final String APP_NAME_PREFIX = "Google-MyTracks-";
private static final String SQL_KEY = "sql=";
@@ -73,29 +73,12 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
private SendFusionTablesActivity activity;
private final Context context;
private final long trackId;
private final Account account;
private final MyTracksProviderUtils myTracksProviderUtils;
private final HttpRequestFactory httpRequestFactory;
/**
* True if can retry sending to Google Fusion Tables.
*/
private boolean canRetry;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if the result is success.
*/
private boolean success;
// The following variables are for per upload states
private String authToken;
private String tableId;
@@ -103,7 +86,7 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
public SendFusionTablesAsyncTask(
SendFusionTablesActivity activity, long trackId, Account account) {
this.activity = activity;
super(activity);
this.trackId = trackId;
this.account = account;
@@ -111,65 +94,26 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
HttpTransport transport = ApiAdapterFactory.getApiAdapter().getHttpTransport();
httpRequestFactory = transport.createRequestFactory(new MethodOverride());
canRetry = true;
completed = false;
success = false;
}
/**
* Sets the activity associated with this AyncTask.
*
* @param activity the activity.
*/
public void setActivity(SendFusionTablesActivity activity) {
this.activity = activity;
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success);
@Override
protected void closeConnection() {
// No action needed for Google Fusion Tables
}
@Override
protected void saveResult() {
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null) {
track.setTableId(tableId);
myTracksProviderUtils.updateTrack(track);
} else {
Log.d(TAG, "No track");
}
}
@Override
protected void onPreExecute() {
activity.showProgressDialog();
}
@Override
protected Boolean doInBackground(Void... params) {
return doUpload();
}
@Override
protected void onProgressUpdate(Integer... values) {
if (activity != null) {
activity.setProgressDialogValue(values[0]);
}
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
completed = true;
if (success) {
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null) {
track.setTableId(tableId);
myTracksProviderUtils.updateTrack(track);
} else {
Log.d(TAG, "No track");
}
}
if (activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
/**
* Uploads a track to Google Fusion Tables.
*
* @return true if success.
*/
private boolean doUpload() {
protected boolean performTask() {
// Reset the per upload states
authToken = null;
tableId = null;
@@ -179,13 +123,13 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
authToken = AccountManager.get(context).blockingGetAuthToken(account, SERVICE_ID, false);
} catch (OperationCanceledException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (AuthenticatorException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (IOException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
}
Track track = myTracksProviderUtils.getTrack(trackId);
@@ -198,7 +142,7 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
publishProgress(PROGRESS_CREATE_TABLE);
if (!createNewTable(track)) {
// Retry upload in case the auth token is invalid
return retryUpload();
return retryTask();
}
// Unlist table
@@ -223,22 +167,9 @@ public class SendFusionTablesAsyncTask extends AsyncTask<Void, Integer, Boolean>
return true;
}
/**
* Retries upload. Invalidates the authToken. If can retry, invokes
* {@link SendFusionTablesAsyncTask#doUpload()}. Returns false if cannot
* retry.
*/
private boolean retryUpload() {
if (isCancelled()) {
return false;
}
@Override
protected void invalidateToken() {
AccountManager.get(context).invalidateAuthToken(SERVICE_ID, authToken);
if (canRetry) {
canRetry = false;
return doUpload();
}
return false;
}
/**
@@ -32,7 +32,7 @@ import java.util.Locale;
/**
* Utilities for sending a track to Google Fusion Tables.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendFusionTablesUtils {
@@ -41,7 +41,7 @@ import java.util.ArrayList;
/**
* An activity to choose a Google Map.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class ChooseMapActivity extends Activity {
@@ -208,7 +208,7 @@ public class ChooseMapActivity extends Activity {
/**
* A class containing {@link ChooseMapActivity} list item.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
private class ListItem {
private String mapId;
@@ -42,7 +42,7 @@ import java.util.ArrayList;
/**
* AsyncTask for {@link ChooseMapActivity} to get all the maps from Google Maps.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ChooseMapAsyncTask.class.getSimpleName();
@@ -17,111 +17,36 @@ package com.google.android.apps.mytracks.io.maps;
import com.google.android.apps.mytracks.io.docs.SendDocsActivity;
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
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;
/**
* An activity to send a track to Google Maps.
*
* @author Jimmy Shih
*/
public class SendMapsActivity extends Activity {
private static final int PROGRESS_DIALOG = 1;
private SendRequest sendRequest;
private SendMapsAsyncTask asyncTask;
private ProgressDialog progressDialog;
public class SendMapsActivity extends AbstractSendActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof SendMapsAsyncTask) {
asyncTask = (SendMapsAsyncTask) retained;
asyncTask.setActivity(this);
} else {
asyncTask = new SendMapsAsyncTask(
this, sendRequest.getTrackId(), sendRequest.getAccount(), sendRequest.getMapId());
asyncTask.execute();
}
protected AbstractSendAsyncTask createAsyncTask() {
return new SendMapsAsyncTask(
this, sendRequest.getTrackId(), sendRequest.getAccount(), sendRequest.getMapId());
}
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.setActivity(null);
return asyncTask;
protected String getServiceName() {
return getString(R.string.send_google_maps);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(
getString(R.string.send_google_progress_title, getString(R.string.send_google_maps)));
progressDialog.setMax(100);
progressDialog.setProgress(0);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
startNextActivity(false, true);
}
});
return progressDialog;
default:
return null;
}
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param success true if success
*/
public void onAsyncTaskCompleted(boolean success) {
startNextActivity(success, false);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(PROGRESS_DIALOG);
}
/**
* Sets the progress dialog value.
*/
public void setProgressDialogValue(int value) {
if (progressDialog != null) {
progressDialog.setProgress(value);
}
}
/**
* Starts the next activity.
*
* @param success true if sendMaps is success
* @param isCancel true if it is a cancel request
*/
private void startNextActivity(boolean success, boolean isCancel) {
protected void startNextActivity(boolean success, boolean isCancel) {
sendRequest.setMapsSuccess(success);
Class<?> next;
if (isCancel) {
next = UploadResultActivity.class;
@@ -134,8 +59,7 @@ public class SendMapsActivity extends Activity {
next = UploadResultActivity.class;
}
}
Intent intent = new Intent(this, next)
.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
Intent intent = new Intent(this, next).putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
startActivity(intent);
finish();
}
@@ -24,6 +24,7 @@ import com.google.android.apps.mytracks.io.gdata.maps.MapsClient;
import com.google.android.apps.mytracks.io.gdata.maps.MapsConstants;
import com.google.android.apps.mytracks.io.gdata.maps.MapsGDataConverter;
import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
import com.google.android.apps.mytracks.stats.DoubleBuffer;
import com.google.android.apps.mytracks.util.LocationUtils;
@@ -43,7 +44,6 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
@@ -63,7 +63,7 @@ import org.xmlpull.v1.XmlPullParserException;
*
* @author Jimmy Shih
*/
public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
public class SendMapsAsyncTask extends AbstractSendAsyncTask {
private static final String START_ICON_URL =
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
private static final String END_ICON_URL =
@@ -78,7 +78,6 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = SendMapsAsyncTask.class.getSimpleName();
private SendMapsActivity activity;
private final long trackId;
private final Account account;
private final String chooseMapId;
@@ -87,30 +86,15 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private final GDataClient gDataClient;
private final MapsClient mapsClient;
/**
* True if can retry sending to Google Maps.
*/
private boolean canRetry;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if the result is success.
*/
private boolean success;
// The following variables are for per upload states
private MapsGDataConverter mapsGDataConverter;
private String authToken;
private String mapId;
int currentSegment;
public SendMapsAsyncTask(
public SendMapsAsyncTask (
SendMapsActivity activity, long trackId, Account account, String chooseMapId) {
this.activity = activity;
super(activity);
this.trackId = trackId;
this.account = account;
this.chooseMapId = chooseMapId;
@@ -120,80 +104,28 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
gDataClient = GDataClientFactory.getGDataClient(context);
mapsClient = new MapsClient(
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
canRetry = true;
completed = false;
success = false;
}
/**
* Sets the activity associated with this AyncTask.
*
* @param activity the activity.
*/
public void setActivity(SendMapsActivity activity) {
this.activity = activity;
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onPreExecute() {
activity.showProgressDialog();
}
@Override
protected Boolean doInBackground(Void... params) {
return doUpload();
}
@Override
protected void onProgressUpdate(Integer... values) {
if (activity != null) {
activity.setProgressDialogValue(values[0]);
}
}
@Override
protected void onPostExecute(Boolean result) {
closeClient();
success = result;
completed = true;
if (success) {
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null) {
track.setMapId(mapId);
myTracksProviderUtils.updateTrack(track);
} else {
Log.d(TAG, "No track");
}
}
if (activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onCancelled() {
closeClient();
}
/**
* Closes the gdata client.
*/
private void closeClient() {
protected void closeConnection() {
if (gDataClient != null) {
gDataClient.close();
}
}
/**
* Uploads a track to Google Maps.
*
* @return true if success.
*/
private boolean doUpload() {
@Override
protected void saveResult() {
Track track = myTracksProviderUtils.getTrack(trackId);
if (track != null) {
track.setMapId(mapId);
myTracksProviderUtils.updateTrack(track);
} else {
Log.d(TAG, "No track");
}
}
@Override
protected boolean performTask() {
// Reset the per upload states
mapsGDataConverter = null;
authToken = null;
@@ -214,13 +146,13 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
account, MapsConstants.SERVICE_NAME, false);
} catch (OperationCanceledException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (AuthenticatorException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
} catch (IOException e) {
Log.d(TAG, "Unable to get auth token", e);
return retryUpload();
return retryTask();
}
// Get the track
@@ -234,14 +166,14 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
publishProgress(PROGRESS_FETCH_MAP_ID);
if (!fetchSendMapId(track)) {
Log.d("TAG", "Unable to upload all track points");
return retryUpload();
return retryTask();
}
// Upload all the track points plus the start and end markers
publishProgress(PROGRESS_UPLOAD_DATA_MIN);
if (!uploadAllTrackPoints(track)) {
Log.d("TAG", "Unable to upload all track points");
return retryUpload();
return retryTask();
}
// Upload all the waypoints
@@ -255,21 +187,9 @@ public class SendMapsAsyncTask extends AsyncTask<Void, Integer, Boolean> {
return true;
}
/**
* Retries upload. Invalidates the authToken. If can retry, invokes
* {@link SendMapsAsyncTask#doUpload()}. Returns false if cannot retry.
*/
private boolean retryUpload() {
if (isCancelled()) {
return false;
}
@Override
protected void invalidateToken() {
AccountManager.get(context).invalidateAuthToken(MapsConstants.SERVICE_NAME, authToken);
if (canRetry) {
canRetry = false;
return doUpload();
}
return false;
}
/**
@@ -37,7 +37,7 @@ import java.util.ArrayList;
/**
* Utilities for sending a track to Google Maps.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendMapsUtils {
@@ -0,0 +1,139 @@
/*
* 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.sendtogoogle;
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;
/**
* The abstract class for activities sending a track to Google.
* <p>
* The activity gets recreated when the screen rotates. To support the activity
* displaying a progress dialog, we do the following:
* <ul>
* <li>use one instance of an AyncTask to send the track</li>
* <li>save that instance as the last non configuration instance of the activity
* </li>
* <li>when a new activity is created, pass the activity to the AsyncTask so
* that the AsyncTask can update the progress dialog of the activity</li>
* </ul>
*
* @author Jimmy Shih
*/
public abstract class AbstractSendActivity extends Activity {
private static final int PROGRESS_DIALOG = 1;
protected SendRequest sendRequest;
private AbstractSendAsyncTask asyncTask;
private ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof AbstractSendAsyncTask) {
asyncTask = (AbstractSendAsyncTask) retained;
asyncTask.setActivity(this);
} else {
asyncTask = createAsyncTask();
asyncTask.execute();
}
}
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.setActivity(null);
return asyncTask;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setIcon(android.R.drawable.ic_dialog_info);
progressDialog.setTitle(getString(R.string.send_google_progress_title, getServiceName()));
progressDialog.setMax(100);
progressDialog.setProgress(0);
progressDialog.setCancelable(true);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
asyncTask.cancel(true);
startNextActivity(false, true);
}
});
return progressDialog;
default:
return null;
}
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param success true if the AsyncTask is successful
*/
public void onAsyncTaskCompleted(boolean success) {
startNextActivity(success, false);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(PROGRESS_DIALOG);
}
/**
* Sets the progress dialog value.
*
* @param value the dialog value
*/
public void setProgressDialogValue(int value) {
if (progressDialog != null) {
progressDialog.setProgress(value);
}
}
/**
* Creates the AsyncTask.
*/
protected abstract AbstractSendAsyncTask createAsyncTask();
/**
* Gets the service name.
*/
protected abstract String getServiceName();
/**
* Starts the next activity.
*
* @param success true if this activity is successful
* @param isCancel true if it is a cancel request
*/
protected abstract void startNextActivity(boolean success, boolean isCancel);
}
@@ -0,0 +1,147 @@
/*
* 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.sendtogoogle;
import android.os.AsyncTask;
/**
* The abstract class for AsyncTasks sending a track to Google.
*
* @author Jimmy Shih
*/
public abstract class AbstractSendAsyncTask extends AsyncTask<Void, Integer, Boolean> {
/**
* The activity associated with this AsyncTask.
*/
private AbstractSendActivity activity;
/**
* True if the AsyncTask result is success.
*/
private boolean success;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if can retry the AsyncTask.
*/
private boolean canRetry;
/**
* Creates an AsyncTask.
*
* @param activity the activity currently associated with this AsyncTask
*/
public AbstractSendAsyncTask(AbstractSendActivity activity) {
this.activity = activity;
success = false;
completed = false;
canRetry = true;
}
/**
* Sets the current activity associated with this AyncTask.
*
* @param activity the current activity, can be null
*/
public void setActivity(AbstractSendActivity activity) {
this.activity = activity;
if (completed && activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onPreExecute() {
activity.showProgressDialog();
}
@Override
protected Boolean doInBackground(Void... params) {
return performTask();
}
@Override
protected void onProgressUpdate(Integer... values) {
if (activity != null) {
activity.setProgressDialogValue(values[0]);
}
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
if (success) {
saveResult();
}
completed = true;
closeConnection();
if (activity != null) {
activity.onAsyncTaskCompleted(success);
}
}
@Override
protected void onCancelled() {
closeConnection();
}
/**
* Retries the task. First, invalidates the auth token. If can retry, invokes
* {@link #performTask()}. Returns false if cannot retry.
*
* @return the result of the retry.
*/
protected boolean retryTask() {
if (isCancelled()) {
return false;
}
invalidateToken();
if (canRetry) {
canRetry = false;
return performTask();
}
return false;
}
/**
* Closes any AsyncTask connection.
*/
protected abstract void closeConnection();
/**
* Saves any AsyncTask result.
*/
protected abstract void saveResult();
/**
* Performs the AsyncTask.
*
* @return true if success
*/
protected abstract boolean performTask();
/**
* Invalidates the auth token.
*/
protected abstract void invalidateToken();
}
@@ -38,7 +38,7 @@ import android.os.Bundle;
/**
* A chooser to select an account.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class AccountChooserActivity extends Activity {
@@ -38,7 +38,7 @@ import android.widget.TextView;
/**
* A dialog to show the result of uploading to Google services.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class UploadResultActivity extends Activity {
@@ -41,7 +41,7 @@ import android.widget.TableRow;
/**
* A chooser to select the Google services to upload a track to.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class UploadServiceChooserActivity extends Activity {
@@ -37,7 +37,7 @@ import android.util.Log;
* application delegates starting and stopping a recording to
* {@link TrackRecordingService} using RPC calls.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class ControlRecordingService extends IntentService implements ServiceConnection {
@@ -26,7 +26,7 @@ import android.test.AndroidTestCase;
/**
* Tests {@link SendDocsUtils}.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendDocsUtilsTest extends AndroidTestCase {
@@ -16,7 +16,6 @@
package com.google.android.apps.mytracks.io.fusiontables;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesUtils;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.location.Location;
@@ -30,7 +29,7 @@ import junit.framework.TestCase;
/**
* Tests {@link SendFusionTablesUtils}.
*
* @author jshih@google.com (Jimmy Shih)
* @author Jimmy Shih
*/
public class SendFusionTablesUtilsTest extends TestCase {