forked from upstream-mirrors/OpenTracks
Update GpxImporter to support tracks with pauses.
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An activity to choose a Google Map.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChooseMapActivity extends Activity {
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_ERROR_ID = 1;
|
||||
|
||||
private SendRequest sendRequest;
|
||||
private ChooseMapAsyncTask asyncTask;
|
||||
@VisibleForTesting
|
||||
ArrayAdapter<ListItem> arrayAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY);
|
||||
setContentView(R.layout.choose_map);
|
||||
|
||||
arrayAdapter = new ArrayAdapter<ListItem>(this, R.layout.choose_map_item, new ArrayList<
|
||||
ListItem>()) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = getLayoutInflater().inflate(R.layout.choose_map_item, parent, false);
|
||||
}
|
||||
MapsMapMetadata mapData = getItem(position).getMapData();
|
||||
|
||||
TextView title = (TextView) convertView.findViewById(R.id.choose_map_item_title);
|
||||
title.setText(mapData.getTitle());
|
||||
|
||||
TextView description = (TextView) convertView.findViewById(
|
||||
R.id.choose_map_item_description);
|
||||
String descriptionText = mapData.getDescription();
|
||||
if (descriptionText == null || descriptionText.equals("")) {
|
||||
description.setVisibility(View.GONE);
|
||||
} else {
|
||||
description.setVisibility(View.VISIBLE);
|
||||
description.setText(descriptionText);
|
||||
}
|
||||
|
||||
TextView searchStatus = (TextView) convertView.findViewById(
|
||||
R.id.choose_map_item_search_status);
|
||||
searchStatus.setTextColor(mapData.getSearchable() ? Color.RED : Color.GREEN);
|
||||
searchStatus.setText(mapData.getSearchable() ? R.string.maps_list_public_label
|
||||
: R.string.maps_list_unlisted_label);
|
||||
return convertView;
|
||||
}
|
||||
};
|
||||
|
||||
ListView list = (ListView) findViewById(R.id.choose_map_list_view);
|
||||
list.setEmptyView(findViewById(R.id.choose_map_empty_view));
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
startNextActivity(arrayAdapter.getItem(position).getMapId());
|
||||
}
|
||||
});
|
||||
list.setAdapter(arrayAdapter);
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof ChooseMapAsyncTask) {
|
||||
asyncTask = (ChooseMapAsyncTask) retained;
|
||||
asyncTask.setActivity(this);
|
||||
} else {
|
||||
asyncTask = new ChooseMapAsyncTask(this, sendRequest.getAccount());
|
||||
asyncTask.execute();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
asyncTask.setActivity(null);
|
||||
return asyncTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_PROGRESS_ID:
|
||||
return DialogUtils.createSpinnerProgressDialog(
|
||||
this, R.string.maps_list_progress_message, new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
asyncTask.cancel(true);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
case DIALOG_ERROR_ID:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(R.string.maps_list_error)
|
||||
.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 arg1) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.generic_error_title)
|
||||
.create();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes when the associated AsyncTask completes.
|
||||
*
|
||||
* @param success true if success
|
||||
* @param mapIds an array of map ids
|
||||
* @param mapData an array of map data
|
||||
*/
|
||||
public void onAsyncTaskCompleted(
|
||||
boolean success, ArrayList<String> mapIds, ArrayList<MapsMapMetadata> mapData) {
|
||||
removeProgressDialog();
|
||||
if (success) {
|
||||
arrayAdapter.clear();
|
||||
// To prevent displaying the emptyView message momentarily before the
|
||||
// arrayAdapter is set, don't set the emptyView message in the xml layout.
|
||||
// Instead, set it only when needed.
|
||||
if (mapIds.size() == 0) {
|
||||
TextView emptyView = (TextView) findViewById(R.id.choose_map_empty_view);
|
||||
emptyView.setText(R.string.maps_list_no_maps);
|
||||
} else {
|
||||
for (int i = 0; i < mapIds.size(); i++) {
|
||||
arrayAdapter.add(new ListItem(mapIds.get(i), mapData.get(i)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showErrorDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the progress dialog.
|
||||
*/
|
||||
public void showProgressDialog() {
|
||||
showDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the error dialog.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void showErrorDialog() {
|
||||
showDialog(DIALOG_ERROR_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the progress dialog.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void removeProgressDialog() {
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the next activity, {@link SendMapsActivity}.
|
||||
*
|
||||
* @param mapId the chosen map id
|
||||
*/
|
||||
private void startNextActivity(String mapId) {
|
||||
sendRequest.setMapId(mapId);
|
||||
Intent intent = IntentUtils.newIntent(this, SendMapsActivity.class)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* A class containing {@link ChooseMapActivity} list item.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class ListItem {
|
||||
private String mapId;
|
||||
private MapsMapMetadata mapData;
|
||||
|
||||
private ListItem(String mapId, MapsMapMetadata mapData) {
|
||||
this.mapId = mapId;
|
||||
this.mapData = mapData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map id.
|
||||
*/
|
||||
public String getMapId() {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map data.
|
||||
*/
|
||||
public MapsMapMetadata getMapData() {
|
||||
return mapData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapFeatureEntry;
|
||||
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.MapsMapMetadata;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.parser.GDataParser;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
|
||||
import android.accounts.Account;
|
||||
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;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* AsyncTask for {@link ChooseMapActivity} to get all the maps from Google Maps.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChooseMapAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
private static final String TAG = ChooseMapAsyncTask.class.getSimpleName();
|
||||
|
||||
private ChooseMapActivity activity;
|
||||
private final Account account;
|
||||
private final Context context;
|
||||
private final GDataClient gDataClient;
|
||||
private final MapsClient mapsClient;
|
||||
|
||||
/**
|
||||
* 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 request states
|
||||
private String authToken;
|
||||
private ArrayList<String> mapIds;
|
||||
private ArrayList<MapsMapMetadata> mapData;
|
||||
|
||||
public ChooseMapAsyncTask(ChooseMapActivity activity, Account account) {
|
||||
this(activity, account, activity.getApplicationContext(), GDataClientFactory
|
||||
.getGDataClient(activity.getApplicationContext()), new MapsClient(
|
||||
GDataClientFactory.getGDataClient(activity.getApplicationContext()),
|
||||
new XmlMapsGDataParserFactory(new AndroidXmlParserFactory())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this constructor for test.
|
||||
*/
|
||||
public ChooseMapAsyncTask(ChooseMapActivity activity, Account account,
|
||||
Context context, GDataClient gDataClient, MapsClient mapsClient) {
|
||||
this.activity = activity;
|
||||
this.account = account;
|
||||
|
||||
this.context = context;
|
||||
this.gDataClient = gDataClient;
|
||||
this.mapsClient = mapsClient;
|
||||
|
||||
canRetry = true;
|
||||
completed = false;
|
||||
success = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the activity associated with this AyncTask.
|
||||
*
|
||||
* @param activity the activity.
|
||||
*/
|
||||
public void setActivity(ChooseMapActivity activity) {
|
||||
this.activity = activity;
|
||||
if (completed && activity != null) {
|
||||
activity.onAsyncTaskCompleted(success, mapIds, mapData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
activity.showProgressDialog();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
return getMaps();
|
||||
} finally {
|
||||
if (gDataClient != null) {
|
||||
gDataClient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
success = result;
|
||||
completed = true;
|
||||
if (activity != null) {
|
||||
activity.onAsyncTaskCompleted(success, mapIds, mapData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the maps from Google Maps.
|
||||
*
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean getMaps() {
|
||||
// Reset the per request states
|
||||
authToken = null;
|
||||
mapIds = new ArrayList<String>();
|
||||
mapData = new ArrayList<MapsMapMetadata>();
|
||||
|
||||
try {
|
||||
authToken = AccountManager.get(context).blockingGetAuthToken(
|
||||
account, MapsConstants.SERVICE_NAME, false);
|
||||
} catch (OperationCanceledException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryUpload();
|
||||
} catch (AuthenticatorException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryUpload();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryUpload();
|
||||
}
|
||||
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
GDataParser gDataParser = null;
|
||||
try {
|
||||
gDataParser = mapsClient.getParserForFeed(
|
||||
MapFeatureEntry.class, MapsClient.getMapsFeed(), authToken);
|
||||
gDataParser.init();
|
||||
while (gDataParser.hasMoreData()) {
|
||||
MapFeatureEntry entry = (MapFeatureEntry) gDataParser.readNextEntry(null);
|
||||
mapIds.add(MapsGDataConverter.getMapidForEntry(entry));
|
||||
mapData.add(MapsGDataConverter.getMapMetadataForEntry(entry));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "Unable to get maps", e);
|
||||
return retryUpload();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get maps", e);
|
||||
return retryUpload();
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Unable to get maps", e);
|
||||
return retryUpload();
|
||||
} finally {
|
||||
if (gDataParser != null) {
|
||||
gDataParser.close();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries upload. Invalidates the authToken. If can retry, invokes
|
||||
* {@link ChooseMapAsyncTask#getMaps()}. Returns false if cannot retry.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean retryUpload() {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AccountManager.get(context).invalidateAuthToken(Constants.ACCOUNT_TYPE, authToken);
|
||||
if (canRetry) {
|
||||
canRetry = false;
|
||||
return getMaps();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the complete status of task.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the complete status of task.
|
||||
* @param completed
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setCompleted(boolean completed) {
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of canRetry.
|
||||
* @param canRetry status of canRetry
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setCanRetry(boolean canRetry) {
|
||||
this.canRetry = canRetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of canRetry.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean getCanRetry() {
|
||||
return canRetry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.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.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* An activity to send a track to Google Maps.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SendMapsActivity extends AbstractSendActivity {
|
||||
|
||||
@Override
|
||||
protected AbstractSendAsyncTask createAsyncTask() {
|
||||
return new SendMapsAsyncTask(
|
||||
this, sendRequest.getTrackId(), sendRequest.getAccount(), sendRequest.getMapId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getServiceName() {
|
||||
return getString(R.string.send_google_maps);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startNextActivity(boolean success, boolean isCancel) {
|
||||
sendRequest.setMapsSuccess(success);
|
||||
Class<?> next = getNextClass(sendRequest, isCancel);
|
||||
Intent intent = IntentUtils.newIntent(this, next)
|
||||
.putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Class<?> getNextClass(SendRequest request, boolean isCancel) {
|
||||
if (isCancel) {
|
||||
return UploadResultActivity.class;
|
||||
} else {
|
||||
if (request.isSendFusionTables()) {
|
||||
return SendFusionTablesActivity.class;
|
||||
} else if (request.isSendDocs()) {
|
||||
return SendDocsActivity.class;
|
||||
} else {
|
||||
return UploadResultActivity.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* 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.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGenerator;
|
||||
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
|
||||
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.gdata.GDataClientFactory;
|
||||
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.TripStatisticsUpdater;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.wireless.gdata.client.GDataClient;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* AsyncTask to send a track to Google Maps.
|
||||
* <p>
|
||||
* IMPORTANT: While this code is Apache-licensed, please notice that usage of
|
||||
* the Google Maps servers through this API is only allowed for the My Tracks
|
||||
* application. Other applications looking to upload maps data should look into
|
||||
* using the Google Fusion Tables API.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
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 = "http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 500;
|
||||
|
||||
private static final int PROGRESS_FETCH_MAP_ID = 5;
|
||||
@VisibleForTesting
|
||||
static final int PROGRESS_UPLOAD_DATA_MIN = 10;
|
||||
@VisibleForTesting
|
||||
static final int PROGRESS_UPLOAD_DATA_MAX = 90;
|
||||
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
||||
private static final int PROGRESS_COMPLETE = 100;
|
||||
|
||||
private static final String TAG = SendMapsAsyncTask.class.getSimpleName();
|
||||
|
||||
private final long trackId;
|
||||
private final Account account;
|
||||
private final String chooseMapId;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private final Context context;
|
||||
private final GDataClient gDataClient;
|
||||
private final MapsClient mapsClient;
|
||||
|
||||
// The following variables are for per upload states
|
||||
private MapsGDataConverter mapsGDataConverter;
|
||||
private String authToken;
|
||||
private String mapId;
|
||||
int currentSegment;
|
||||
|
||||
public SendMapsAsyncTask(
|
||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId) {
|
||||
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(
|
||||
activity.getApplicationContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is created for test.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
||||
String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
||||
super(activity);
|
||||
this.trackId = trackId;
|
||||
this.account = account;
|
||||
this.chooseMapId = chooseMapId;
|
||||
this.myTracksProviderUtils = myTracksProviderUtils;
|
||||
context = activity.getApplicationContext();
|
||||
|
||||
gDataClient = GDataClientFactory.getGDataClient(context);
|
||||
mapsClient = new MapsClient(
|
||||
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeConnection() {
|
||||
if (gDataClient != null) {
|
||||
gDataClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveResult() {
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.d(TAG, "No track for " + trackId);
|
||||
return;
|
||||
}
|
||||
track.setMapId(mapId);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean performTask() {
|
||||
// Reset the per upload states
|
||||
mapsGDataConverter = null;
|
||||
authToken = null;
|
||||
mapId = null;
|
||||
currentSegment = 1;
|
||||
|
||||
// Create a maps gdata converter
|
||||
try {
|
||||
mapsGDataConverter = new MapsGDataConverter();
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.d(TAG, "Unable to create a maps gdata converter", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get auth token
|
||||
try {
|
||||
authToken = AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false);
|
||||
} catch (OperationCanceledException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
} catch (AuthenticatorException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
}
|
||||
|
||||
// Get the track
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.d(TAG, "No track for " + trackId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch the mapId, create a new map if necessary
|
||||
publishProgress(PROGRESS_FETCH_MAP_ID);
|
||||
if (!fetchSendMapId(track)) {
|
||||
Log.d("TAG", "Unable to upload all track points");
|
||||
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 retryTask();
|
||||
}
|
||||
|
||||
// Upload all the waypoints
|
||||
publishProgress(PROGRESS_UPLOAD_WAYPOINTS);
|
||||
if (!uploadWaypoints()) {
|
||||
Log.d("TAG", "Unable to upload waypoints");
|
||||
return false;
|
||||
}
|
||||
|
||||
publishProgress(PROGRESS_COMPLETE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateToken() {
|
||||
AccountManager.get(context).invalidateAuthToken(Constants.ACCOUNT_TYPE, authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the {@link SendMapsAsyncTask#mapId} instance variable for sending a
|
||||
* track to Google Maps.
|
||||
*
|
||||
* @param track the Track
|
||||
* @return true if able to fetch the mapId variable.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean fetchSendMapId(Track track) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chooseMapId != null) {
|
||||
mapId = chooseMapId;
|
||||
return true;
|
||||
} else {
|
||||
boolean defaultMapPublic = PreferencesUtils.getBoolean(
|
||||
context, R.string.default_map_public_key, PreferencesUtils.DEFAULT_MAP_PUBLIC_DEFAULT);
|
||||
try {
|
||||
String description = track.getCategory() + "\n" + track.getDescription() + "\n"
|
||||
+ context.getString(R.string.send_google_by_my_tracks, "", "");
|
||||
mapId = SendMapsUtils.createNewMap(
|
||||
track.getName(), description, defaultMapPublic, mapsClient, authToken);
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "Unable to create a new map", e);
|
||||
return false;
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Unable to create a new map", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to create a new map", e);
|
||||
return false;
|
||||
}
|
||||
return mapId != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all the points in a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean uploadAllTrackPoints(Track track) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTrackPointCursor(trackId, 0, -1, false);
|
||||
if (cursor == null) {
|
||||
Log.d(TAG, "Location cursor is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = cursor.getCount();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastLocation = null;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0));
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
TripStatisticsUpdater tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
int minRecordingDistance = PreferencesUtils.getInt(context,
|
||||
R.string.min_recording_distance_key, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
Location location = myTracksProviderUtils.createTrackPoint(cursor);
|
||||
locations.add(location);
|
||||
|
||||
if (i == 0) {
|
||||
// Create a start marker
|
||||
if (!uploadMarker(context.getString(R.string.marker_label_start, track.getName()), "",
|
||||
START_ICON_URL, location)) {
|
||||
Log.d(TAG, "Unable to create a start marker");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tripStatisticsUpdater.addLocation(location, minRecordingDistance);
|
||||
if (i % elevationSamplingFrequency == 0) {
|
||||
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
|
||||
elevations.add(tripStatisticsUpdater.getSmoothedElevation());
|
||||
}
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
// Upload periodically
|
||||
int readCount = i + 1;
|
||||
if (readCount % MAX_POINTS_PER_UPLOAD == 0) {
|
||||
if (!prepareAndUploadPoints(track, locations, false)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
updateProgress(readCount, count);
|
||||
locations.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Do a final upload with the remaining locations
|
||||
if (!prepareAndUploadPoints(track, locations, true)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an end marker
|
||||
if (lastLocation != null) {
|
||||
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
|
||||
elevations.add(tripStatisticsUpdater.getSmoothedElevation());
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
track.setDescription(
|
||||
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
|
||||
track.getDescription(), END_ICON_URL, lastLocation)) {
|
||||
Log.d(TAG, "Unable to create an end marker");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and uploads a list of locations from a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @param locations the locations from the track
|
||||
* @param lastBatch true if it is the last batch of locations
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean prepareAndUploadPoints(Track track, List<Location> locations, boolean lastBatch) {
|
||||
// Prepare locations
|
||||
ArrayList<Track> splitTracks = SendToGoogleUtils.prepareLocations(track, locations);
|
||||
|
||||
// Upload segments
|
||||
boolean onlyOneSegment = lastBatch && currentSegment == 1 && splitTracks.size() == 1;
|
||||
for (Track segment : splitTracks) {
|
||||
if (!onlyOneSegment) {
|
||||
segment.setName(context.getString(
|
||||
R.string.send_google_track_part_label, segment.getName(), currentSegment));
|
||||
}
|
||||
if (!uploadSegment(segment.getName(), segment.getLocations())) {
|
||||
Log.d(TAG, "Unable to upload segment");
|
||||
return false;
|
||||
}
|
||||
currentSegment++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a marker.
|
||||
*
|
||||
* @param title marker title
|
||||
* @param description marker description
|
||||
* @param iconUrl marker marker icon
|
||||
* @param location marker location
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean uploadMarker(String title, String description, String iconUrl, Location location) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (!SendMapsUtils.uploadMarker(mapId, title, description, iconUrl, location, mapsClient,
|
||||
authToken, mapsGDataConverter)) {
|
||||
Log.d(TAG, "Unable to upload marker");
|
||||
return false;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "Unable to upload marker", e);
|
||||
return false;
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Unable to upload marker", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to upload marker", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a segment
|
||||
*
|
||||
* @param title segment title
|
||||
* @param locations segment locations
|
||||
* @return true if success
|
||||
*/
|
||||
private boolean uploadSegment(String title, ArrayList<Location> locations) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (!SendMapsUtils.uploadSegment(
|
||||
mapId, title, locations, mapsClient, authToken, mapsGDataConverter)) {
|
||||
Log.d(TAG, "Unable to upload track points");
|
||||
return false;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "Unable to upload track points", e);
|
||||
return false;
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Unable to upload track points", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to upload track points", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all the waypoints.
|
||||
*
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean uploadWaypoints() {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getWaypointCursor(
|
||||
trackId, 0, Constants.MAX_LOADED_WAYPOINTS_POINTS);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
// This will skip the first waypoint (it carries the stats for the
|
||||
// track).
|
||||
while (cursor.moveToNext()) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
Waypoint waypoint = myTracksProviderUtils.createWaypoint(cursor);
|
||||
try {
|
||||
if (!SendMapsUtils.uploadWaypoint(
|
||||
mapId, waypoint, mapsClient, authToken, mapsGDataConverter)) {
|
||||
Log.d(TAG, "Unable to upload waypoint");
|
||||
return false;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
Log.d(TAG, "Unable to upload waypoint", e);
|
||||
return false;
|
||||
} catch (HttpException e) {
|
||||
Log.d(TAG, "Unable to upload waypoint", e);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to upload waypoint", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the progress based on the number of locations uploaded.
|
||||
*
|
||||
* @param uploaded the number of uploaded locations
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void updateProgress(int uploaded, int total) {
|
||||
publishProgress(getPercentage(uploaded, total));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the percentage of the number of locations uploaded.
|
||||
*
|
||||
* @param uploaded the number of uploaded locations
|
||||
* @param total the number of total locations
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static int getPercentage(int uploaded, int total) {
|
||||
double totalPercentage = (double) uploaded / total;
|
||||
double scaledPercentage = totalPercentage
|
||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||
return (int) scaledPercentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapID.
|
||||
*
|
||||
* @return mapId
|
||||
*/
|
||||
@VisibleForTesting
|
||||
String getMapId() {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of mapsGDataConverter.
|
||||
*
|
||||
* @param mapsGDataConverter new value of mapsGDataConverter
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setMapsGDataConverter(MapsGDataConverter mapsGDataConverter) {
|
||||
this.mapsGDataConverter = mapsGDataConverter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* 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.maps;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsClient;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsFeature;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsGDataConverter;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsMapMetadata;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.wireless.gdata.client.HttpException;
|
||||
import com.google.wireless.gdata.data.Entry;
|
||||
import com.google.wireless.gdata.parser.ParseException;
|
||||
|
||||
import android.location.Location;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utilities for sending a track to Google Maps.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SendMapsUtils {
|
||||
|
||||
private static final String EMPTY_TITLE = "-";
|
||||
private static final int LINE_COLOR = 0x80FF0000;
|
||||
private static final String TAG = SendMapsUtils.class.getSimpleName();
|
||||
|
||||
private SendMapsUtils() {}
|
||||
|
||||
/**
|
||||
* Gets the Google Maps url for a track.
|
||||
*
|
||||
* @param track the track
|
||||
* @return the url if available.
|
||||
*/
|
||||
public static String getMapUrl(Track track) {
|
||||
if (track == null
|
||||
|| track.getMapId() == null
|
||||
|| track.getMapId().length() == 0) {
|
||||
Log.e(TAG, "Invalid track");
|
||||
return null;
|
||||
}
|
||||
return MapsClient.buildMapUrl(track.getMapId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Google Map.
|
||||
*
|
||||
* @param title title of the map
|
||||
* @param description description of the map
|
||||
* @param isPublic true if the map can be public
|
||||
* @param mapsClient the maps client
|
||||
* @param authToken the auth token
|
||||
* @return map id of the created map if successful.
|
||||
*/
|
||||
public static String createNewMap(
|
||||
String title, String description, boolean isPublic, MapsClient mapsClient, String authToken)
|
||||
throws ParseException, HttpException, IOException {
|
||||
String mapFeed = MapsClient.getMapsFeed();
|
||||
MapsMapMetadata metaData = new MapsMapMetadata();
|
||||
metaData.setTitle(title);
|
||||
metaData.setDescription(description);
|
||||
metaData.setSearchable(isPublic);
|
||||
Entry entry = MapsGDataConverter.getMapEntryForMetadata(metaData);
|
||||
Entry result = mapsClient.createEntry(mapFeed, authToken, entry);
|
||||
if (result == null) {
|
||||
Log.d(TAG, "No result when creating a new map");
|
||||
return null;
|
||||
}
|
||||
return MapsClient.getMapIdFromMapEntryId(result.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a start/end marker to Google Maps.
|
||||
*
|
||||
* @param mapId the map id
|
||||
* @param title the marker title
|
||||
* @param description the marker description
|
||||
* @param iconUrl the marker icon URL
|
||||
* @param location the marker location
|
||||
* @param mapsClient the maps client
|
||||
* @param authToken the auth token
|
||||
* @param mapsGDataConverter the maps gdata converter
|
||||
* @return true if success.
|
||||
*/
|
||||
public static boolean uploadMarker(String mapId, String title, String description, String iconUrl,
|
||||
Location location, MapsClient mapsClient, String authToken,
|
||||
MapsGDataConverter mapsGDataConverter) throws ParseException, HttpException, IOException {
|
||||
String featuresFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
MapsFeature mapsFeature = buildMapsMarkerFeature(
|
||||
title, description, iconUrl, getGeoPoint(location));
|
||||
Entry entry = mapsGDataConverter.getEntryForFeature(mapsFeature);
|
||||
try {
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
} catch (IOException e) {
|
||||
// Retry once (often IOException is thrown on a timeout)
|
||||
Log.d(TAG, "Retry upload marker", e);
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a waypoint as a marker feature to Google Maps.
|
||||
*
|
||||
* @param mapId the map id
|
||||
* @param waypoint the waypoint
|
||||
* @param mapsClient the maps client
|
||||
* @param authToken the auth token
|
||||
* @param mapsGDataConverter the maps gdata converter
|
||||
* @return true if success.
|
||||
*/
|
||||
public static boolean uploadWaypoint(String mapId, Waypoint waypoint, MapsClient mapsClient,
|
||||
String authToken, MapsGDataConverter mapsGDataConverter)
|
||||
throws ParseException, HttpException, IOException {
|
||||
String featuresFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
MapsFeature feature = buildMapsMarkerFeature(waypoint.getName(), waypoint.getDescription(),
|
||||
waypoint.getIcon(), getGeoPoint(waypoint.getLocation()));
|
||||
Entry entry = mapsGDataConverter.getEntryForFeature(feature);
|
||||
try {
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
} catch (IOException e) {
|
||||
// Retry once (often IOException is thrown on a timeout)
|
||||
Log.d(TAG, "Retry upload waypoint", e);
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a segment as a line feature to Google Maps.
|
||||
*
|
||||
* @param mapId the map id
|
||||
* @param title the segment title
|
||||
* @param locations the segment locations
|
||||
* @param mapsClient the maps client
|
||||
* @param authToken the auth token
|
||||
* @param mapsGDataConverter the maps gdata converter
|
||||
* @return true if success.
|
||||
*/
|
||||
public static boolean uploadSegment(String mapId, String title, ArrayList<Location> locations,
|
||||
MapsClient mapsClient, String authToken, MapsGDataConverter mapsGDataConverter)
|
||||
throws ParseException, HttpException, IOException {
|
||||
String featuresFeed = MapsClient.getFeaturesFeed(mapId);
|
||||
Entry entry = mapsGDataConverter.getEntryForFeature(buildMapsLineFeature(title, locations));
|
||||
try {
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
} catch (IOException e) {
|
||||
// Retry once (often IOException is thrown on a timeout)
|
||||
Log.d(TAG, "Retry upload track points", e);
|
||||
mapsClient.createEntry(featuresFeed, authToken, entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a map marker feature.
|
||||
*
|
||||
* @param title feature title
|
||||
* @param description the feature description
|
||||
* @param iconUrl the feature icon URL
|
||||
* @param geoPoint the marker
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static MapsFeature buildMapsMarkerFeature(
|
||||
String title, String description, String iconUrl, GeoPoint geoPoint) {
|
||||
MapsFeature mapsFeature = new MapsFeature();
|
||||
mapsFeature.setType(MapsFeature.MARKER);
|
||||
mapsFeature.generateAndroidId();
|
||||
// Feature must have a name (otherwise GData upload may fail)
|
||||
mapsFeature.setTitle(TextUtils.isEmpty(title) ? EMPTY_TITLE : title);
|
||||
mapsFeature.setDescription(description.replaceAll("\n", "<br>"));
|
||||
mapsFeature.setIconUrl(iconUrl);
|
||||
mapsFeature.addPoint(geoPoint);
|
||||
return mapsFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a maps line feature from a set of locations.
|
||||
*
|
||||
* @param title the feature title
|
||||
* @param locations set of locations
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static MapsFeature buildMapsLineFeature(String title, ArrayList<Location> locations) {
|
||||
MapsFeature mapsFeature = new MapsFeature();
|
||||
mapsFeature.setType(MapsFeature.LINE);
|
||||
mapsFeature.generateAndroidId();
|
||||
// Feature must have a name (otherwise GData upload may fail)
|
||||
mapsFeature.setTitle(TextUtils.isEmpty(title) ? EMPTY_TITLE : title);
|
||||
mapsFeature.setColor(LINE_COLOR);
|
||||
for (Location location : locations) {
|
||||
mapsFeature.addPoint(getGeoPoint(location));
|
||||
}
|
||||
return mapsFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link GeoPoint} from a {@link Location}.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static GeoPoint getGeoPoint(Location location) {
|
||||
return new GeoPoint(
|
||||
(int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user