forked from upstream-mirrors/OpenTracks
Merging MapsFacadeImpl into MapsFacade
This commit is contained in:
@@ -19,7 +19,6 @@ import com.google.android.accounts.Account;
|
|||||||
import com.google.android.apps.mytracks.io.AuthManager;
|
import com.google.android.apps.mytracks.io.AuthManager;
|
||||||
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
import com.google.android.apps.mytracks.io.AuthManagerFactory;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacadeImpl;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
|
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
@@ -125,7 +124,7 @@ public class MyMapsList extends Activity implements MapsFacade.MapsListCallback
|
|||||||
auth.doLogin(new Runnable() {
|
auth.doLogin(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
// Runs in UI thread.
|
// Runs in UI thread.
|
||||||
mapsClient = new MapsFacadeImpl(MyMapsList.this, auth);
|
mapsClient = new MapsFacade(MyMapsList.this, auth);
|
||||||
|
|
||||||
startLookup();
|
startLookup();
|
||||||
}
|
}
|
||||||
@@ -200,7 +199,7 @@ public class MyMapsList extends Activity implements MapsFacade.MapsListCallback
|
|||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, String.format(
|
shareIntent.putExtra(Intent.EXTRA_TEXT, String.format(
|
||||||
getText(R.string.share_map_body_format).toString(),
|
getText(R.string.share_map_body_format).toString(),
|
||||||
listItem[1],
|
listItem[1],
|
||||||
MapsFacadeImpl.buildMapUrl(listItem[0])));
|
MapsFacade.buildMapUrl(listItem[0])));
|
||||||
startActivity(Intent.createChooser(shareIntent,
|
startActivity(Intent.createChooser(shareIntent,
|
||||||
getText(R.string.share_map).toString()));
|
getText(R.string.share_map).toString()));
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import com.google.android.apps.mytracks.io.TempFileCleaner;
|
|||||||
import com.google.android.apps.mytracks.io.TrackWriter;
|
import com.google.android.apps.mytracks.io.TrackWriter;
|
||||||
import com.google.android.apps.mytracks.io.TrackWriterFactory;
|
import com.google.android.apps.mytracks.io.TrackWriterFactory;
|
||||||
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
import com.google.android.apps.mytracks.io.TrackWriterFactory.TrackFileFormat;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacadeImpl;
|
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
|
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.ResultDialogFactory;
|
import com.google.android.apps.mytracks.io.sendtogoogle.ResultDialogFactory;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog;
|
||||||
@@ -607,7 +607,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
Track selectedTrack = providerUtils.getTrack(selectedTrackId);
|
Track selectedTrack = providerUtils.getTrack(selectedTrackId);
|
||||||
if (selectedTrack != null) {
|
if (selectedTrack != null) {
|
||||||
if (!TextUtils.isEmpty(selectedTrack.getMapId())) {
|
if (!TextUtils.isEmpty(selectedTrack.getMapId())) {
|
||||||
shareLinkToMap(MapsFacadeImpl.buildMapUrl(selectedTrack.getMapId()));
|
shareLinkToMap(MapsFacade.buildMapUrl(selectedTrack.getMapId()));
|
||||||
} else if (!TextUtils.isEmpty(selectedTrack.getTableId())) {
|
} else if (!TextUtils.isEmpty(selectedTrack.getTableId())) {
|
||||||
shareLinkToMap(getFusionTablesUrl(selectedTrackId));
|
shareLinkToMap(getFusionTablesUrl(selectedTrackId));
|
||||||
} else {
|
} else {
|
||||||
@@ -1226,7 +1226,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
String url = null;
|
String url = null;
|
||||||
if (sentToMyMaps && sendToMyMapsSuccess) {
|
if (sentToMyMaps && sendToMyMapsSuccess) {
|
||||||
// Prefer a link to My Maps
|
// Prefer a link to My Maps
|
||||||
url = MapsFacadeImpl.buildMapUrl(sendToMyMapsMapId);
|
url = MapsFacade.buildMapUrl(sendToMyMapsMapId);
|
||||||
} else if (sentToFusionTables && sendToFusionTablesSuccess) {
|
} else if (sentToFusionTables && sendToFusionTablesSuccess) {
|
||||||
// Otherwise try using the link to fusion tables
|
// Otherwise try using the link to fusion tables
|
||||||
url = getFusionTablesUrl(sendToTrackId);
|
url = getFusionTablesUrl(sendToTrackId);
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
|||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacade.WaypointData;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacadeImpl;
|
|
||||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||||
@@ -112,7 +110,7 @@ public class SendToMyMaps implements Runnable {
|
|||||||
track.setDescription("<p>" + track.getDescription() + "</p><p>"
|
track.setDescription("<p>" + track.getDescription() + "</p><p>"
|
||||||
+ stringUtils.generateTrackDescription(track, null, null) + "</p>");
|
+ stringUtils.generateTrackDescription(track, null, null) + "</p>");
|
||||||
|
|
||||||
mapsClient = new MapsFacadeImpl(context, auth);
|
mapsClient = new MapsFacade(context, auth);
|
||||||
|
|
||||||
// Create a new map if necessary:
|
// Create a new map if necessary:
|
||||||
boolean isNewMap = mapId.equals(NEW_MAP_ID);
|
boolean isNewMap = mapId.equals(NEW_MAP_ID);
|
||||||
@@ -149,11 +147,10 @@ public class SendToMyMaps implements Runnable {
|
|||||||
if (c.getCount() > 1 && c.moveToFirst()) {
|
if (c.getCount() > 1 && c.moveToFirst()) {
|
||||||
// This will skip the 1st waypoint (it carries the stats for the
|
// This will skip the 1st waypoint (it carries the stats for the
|
||||||
// last segment).
|
// last segment).
|
||||||
ArrayList<WaypointData> waypoints = new ArrayList<WaypointData>(c.getCount() - 1);
|
ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>(c.getCount() - 1);
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
Waypoint wpt = providerUtils.createWaypoint(c);
|
Waypoint wpt = providerUtils.createWaypoint(c);
|
||||||
waypoints.add(new WaypointData(
|
waypoints.add(wpt);
|
||||||
wpt.getName(), wpt.getDescription(), wpt.getIcon(), wpt.getLocation()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
success = mapsClient.uploadWaypoints(mapId, waypoints);
|
success = mapsClient.uploadWaypoints(mapId, waypoints);
|
||||||
|
|||||||
@@ -2,17 +2,30 @@
|
|||||||
package com.google.android.apps.mytracks.io.mymaps;
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
|
import com.google.android.apps.mytracks.io.AuthManager;
|
||||||
|
import com.google.android.maps.GeoPoint;
|
||||||
|
import com.google.android.maps.mytracks.R;
|
||||||
|
import com.google.wireless.gdata.client.HttpException;
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
import com.google.wireless.gdata.parser.GDataParser;
|
||||||
|
import com.google.wireless.gdata.parser.ParseException;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single interface which abstracts all access to the Google Maps service.
|
* Single interface which abstracts all access to the Google Maps service.
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public interface MapsFacade {
|
public class MapsFacade {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for receiving data back from getMapsList.
|
* Interface for receiving data back from getMapsList.
|
||||||
@@ -23,13 +36,62 @@ public interface MapsFacade {
|
|||||||
boolean isPublic);
|
boolean isPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String END_ICON_URL =
|
||||||
|
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
||||||
|
private static final String START_ICON_URL =
|
||||||
|
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final MyMapsGDataWrapper wrapper;
|
||||||
|
private final MyMapsGDataConverter gdataConverter;
|
||||||
|
private final String authToken;
|
||||||
|
|
||||||
|
public MapsFacade(Context context, AuthManager auth) {
|
||||||
|
this.context = context;
|
||||||
|
this.authToken = auth.getAuthToken();
|
||||||
|
|
||||||
|
wrapper = new MyMapsGDataWrapper(context, auth);
|
||||||
|
wrapper.setRetryOnAuthFailure(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
gdataConverter = new MyMapsGDataConverter();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
throw new IllegalStateException("Unable to create maps data converter", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildMapUrl(String mapId) {
|
||||||
|
return MapsClient.buildMapUrl(mapId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all maps for the current user.
|
* Returns a list of all maps for the current user.
|
||||||
*
|
*
|
||||||
* @param callback callback to call for each map returned
|
* @param callback callback to call for each map returned
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean getMapsList(MapsListCallback callback);
|
public boolean getMapsList(final MapsListCallback callback) {
|
||||||
|
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
||||||
|
@Override
|
||||||
|
public void query(MapsClient client) throws IOException, Exception {
|
||||||
|
GDataParser listParser = client.getParserForFeed(
|
||||||
|
MapFeatureEntry.class, MapsClient.getMapsFeed(), authToken);
|
||||||
|
listParser.init();
|
||||||
|
while (listParser.hasMoreData()) {
|
||||||
|
MapFeatureEntry entry =
|
||||||
|
(MapFeatureEntry) listParser.readNextEntry(null);
|
||||||
|
MyMapsMapMetadata metadata =
|
||||||
|
MyMapsGDataConverter.getMapMetadataForEntry(entry);
|
||||||
|
String mapId = MyMapsGDataConverter.getMapidForEntry(entry);
|
||||||
|
|
||||||
|
callback.onReceivedMapListing(
|
||||||
|
mapId, metadata.getTitle(), metadata.getDescription(), metadata.getSearchable());
|
||||||
|
}
|
||||||
|
listParser.close();
|
||||||
|
listParser = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new map for the current user.
|
* Creates a new map for the current user.
|
||||||
@@ -41,8 +103,34 @@ public interface MapsFacade {
|
|||||||
* @param mapIdBuilder builder to append the map ID to
|
* @param mapIdBuilder builder to append the map ID to
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean createNewMap(String title, String category, String description,
|
public boolean createNewMap(
|
||||||
boolean isPublic, StringBuilder mapIdBuilder);
|
final String title, final String category, final String description,
|
||||||
|
final boolean isPublic, final StringBuilder mapIdBuilder) {
|
||||||
|
if (mapIdBuilder.length() > 0) {
|
||||||
|
throw new IllegalArgumentException("mapIdBuilder should be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
||||||
|
@Override
|
||||||
|
public void query(MapsClient client) throws IOException, Exception {
|
||||||
|
Log.d(MyMapsConstants.TAG, "Creating a new map.");
|
||||||
|
String mapFeed = MapsClient.getMapsFeed();
|
||||||
|
Log.d(MyMapsConstants.TAG, "Map feed is " + mapFeed);
|
||||||
|
MyMapsMapMetadata metaData = new MyMapsMapMetadata();
|
||||||
|
metaData.setTitle(title);
|
||||||
|
metaData.setDescription(description + " - "
|
||||||
|
+ category + " - " + context.getString(R.string.new_map_description));
|
||||||
|
metaData.setSearchable(isPublic);
|
||||||
|
Entry entry = MyMapsGDataConverter.getMapEntryForMetadata(metaData);
|
||||||
|
Log.d(MyMapsConstants.TAG, "Title: " + entry.getTitle());
|
||||||
|
Entry map = client.createEntry(mapFeed, authToken, entry);
|
||||||
|
|
||||||
|
String mapId = MapsClient.getMapIdFromMapEntryId(map.getId());
|
||||||
|
mapIdBuilder.append(mapId);
|
||||||
|
Log.d(MyMapsConstants.TAG, "New map id is: " + mapId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a single start or end marker to the given map.
|
* Uploads a single start or end marker to the given map.
|
||||||
@@ -54,8 +142,92 @@ public interface MapsFacade {
|
|||||||
* @param isStart true to add a start marker, false to add an end marker
|
* @param isStart true to add a start marker, false to add an end marker
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean uploadMarker(String mapId, String trackName, String trackDescription,
|
public boolean uploadMarker(final String mapId, final String trackName,
|
||||||
Location location, boolean isStart);
|
final String trackDescription, final Location loc, final boolean isStart) {
|
||||||
|
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
||||||
|
@Override
|
||||||
|
public void query(MapsClient client)
|
||||||
|
throws IOException, Exception {
|
||||||
|
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||||
|
GeoPoint geoPoint = getGeoPoint(loc);
|
||||||
|
insertMarker(client, featureFeed, trackName, trackDescription, geoPoint, isStart);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a place mark. Second try if 1st try fails. Will throw exception on
|
||||||
|
* 2nd failure.
|
||||||
|
*/
|
||||||
|
private void insertMarker(MapsClient client,
|
||||||
|
String featureFeed,
|
||||||
|
String trackName, String trackDescription,
|
||||||
|
GeoPoint geoPoint,
|
||||||
|
boolean isStart) throws IOException, Exception {
|
||||||
|
MyMapsFeature feature =
|
||||||
|
buildMyMapsPlacemarkFeature(trackName, trackDescription, geoPoint, isStart);
|
||||||
|
Entry entry = gdataConverter.getEntryForFeature(feature);
|
||||||
|
Log.d(MyMapsConstants.TAG, "SendToMyMaps: Creating placemark "
|
||||||
|
+ entry.getTitle());
|
||||||
|
try {
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG, "SendToMyMaps: createEntry success!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
||||||
|
// Retry once (often IOException is thrown on a timeout):
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry success on 2nd try!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a placemark MyMapsFeature from a track.
|
||||||
|
*
|
||||||
|
* @param track the track
|
||||||
|
* @param isStart true if it's the start of the track, or false for end
|
||||||
|
* @return a MyMapsFeature
|
||||||
|
*/
|
||||||
|
private MyMapsFeature buildMyMapsPlacemarkFeature(
|
||||||
|
String trackName, String trackDescription,
|
||||||
|
GeoPoint geoPoint, boolean isStart) {
|
||||||
|
String iconUrl;
|
||||||
|
if (isStart) {
|
||||||
|
iconUrl = START_ICON_URL;
|
||||||
|
} else {
|
||||||
|
iconUrl = END_ICON_URL;
|
||||||
|
}
|
||||||
|
String title = trackName + " "
|
||||||
|
+ (isStart ? context.getString(R.string.start)
|
||||||
|
: context.getString(R.string.end));
|
||||||
|
String description = isStart ? "" : trackDescription;
|
||||||
|
return buildMyMapsPlacemarkFeature(title, description, iconUrl, geoPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a MyMapsFeature from a track.
|
||||||
|
*
|
||||||
|
* @param wpt the waypoint
|
||||||
|
* @return a MyMapsFeature
|
||||||
|
*/
|
||||||
|
private static MyMapsFeature buildMyMapsPlacemarkFeature(
|
||||||
|
String title, String description, String iconUrl, GeoPoint geoPoint) {
|
||||||
|
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
||||||
|
myMapsFeature.generateAndroidId();
|
||||||
|
myMapsFeature.setType(MyMapsFeature.MARKER);
|
||||||
|
myMapsFeature.setIconUrl(iconUrl);
|
||||||
|
myMapsFeature.setDescription(description);
|
||||||
|
myMapsFeature.addPoint(geoPoint);
|
||||||
|
if (TextUtils.isEmpty(title)) {
|
||||||
|
// Features must have a name (otherwise GData upload may fail):
|
||||||
|
myMapsFeature.setTitle("-");
|
||||||
|
} else {
|
||||||
|
myMapsFeature.setTitle(title);
|
||||||
|
}
|
||||||
|
myMapsFeature.setDescription(description.replaceAll("\n", "<br>"));
|
||||||
|
return myMapsFeature;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a series of waypoints to the given map.
|
* Uploads a series of waypoints to the given map.
|
||||||
@@ -64,7 +236,49 @@ public interface MapsFacade {
|
|||||||
* @param waypoints the waypoints to upload
|
* @param waypoints the waypoints to upload
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean uploadWaypoints(String mapId, Iterable<Waypoint> waypoints);
|
public boolean uploadWaypoints(
|
||||||
|
final String mapId, final Iterable<Waypoint> waypoints) {
|
||||||
|
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
||||||
|
public void query(MapsClient client) {
|
||||||
|
// TODO(rdamazio): Stream through the waypoints in chunks.
|
||||||
|
// I am leaving the number of waypoints very high which should not be a
|
||||||
|
// problem because we don't try to load them into objects all at the
|
||||||
|
// same time.
|
||||||
|
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (Waypoint waypoint : waypoints) {
|
||||||
|
MyMapsFeature feature = buildMyMapsPlacemarkFeature(
|
||||||
|
waypoint.getName(), waypoint.getDescription(), waypoint.getIcon(),
|
||||||
|
getGeoPoint(waypoint.getLocation()));
|
||||||
|
Entry entry = gdataConverter.getEntryForFeature(feature);
|
||||||
|
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: Creating waypoint.");
|
||||||
|
try {
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry success!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry 1st try failed. Retrying.");
|
||||||
|
|
||||||
|
// Retry once (often IOException is thrown on a timeout):
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry success on 2nd try!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG, "ParseException caught.", e);
|
||||||
|
} catch (HttpException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG, "HttpException caught.", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG, "IOException caught.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a series of points to the given map.
|
* Uploads a series of points to the given map.
|
||||||
@@ -74,12 +288,85 @@ public interface MapsFacade {
|
|||||||
* @param locations the locations to upload
|
* @param locations the locations to upload
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
boolean uploadTrackPoints(String mapId, String trackName,
|
public boolean uploadTrackPoints(
|
||||||
Collection<Location> locations);
|
final String mapId, final String trackName, final Collection<Location> locations) {
|
||||||
|
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
||||||
|
@Override
|
||||||
|
public void query(MapsClient client)
|
||||||
|
throws IOException, Exception {
|
||||||
|
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
||||||
|
Log.d(MyMapsConstants.TAG, "Feature feed url: " + featureFeed);
|
||||||
|
uploadTrackPoints(client, featureFeed, trackName, locations);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean uploadTrackPoints(
|
||||||
|
MapsClient client,
|
||||||
|
String featureFeed,
|
||||||
|
String trackName, Collection<Location> locations)
|
||||||
|
throws IOException, Exception {
|
||||||
|
Entry entry = null;
|
||||||
|
int numLocations = locations.size();
|
||||||
|
if (numLocations < 2) {
|
||||||
|
// Need at least two points for a polyline:
|
||||||
|
Log.w(MyMapsConstants.TAG, "Not uploading too few points");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the line:
|
||||||
|
entry = gdataConverter.getEntryForFeature(
|
||||||
|
buildMyMapsLineFeature(trackName, locations));
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: Creating line " + entry.getTitle());
|
||||||
|
try {
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG, "SendToMyMaps: createEntry success!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
||||||
|
// Retry once (often IOException is thrown on a timeout):
|
||||||
|
client.createEntry(featureFeed, authToken, entry);
|
||||||
|
Log.d(MyMapsConstants.TAG,
|
||||||
|
"SendToMyMaps: createEntry success on 2nd try!");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a MyMapsFeature from a track.
|
||||||
|
*
|
||||||
|
* @param track the track
|
||||||
|
* @return a MyMapsFeature
|
||||||
|
*/
|
||||||
|
private static MyMapsFeature buildMyMapsLineFeature(String trackName,
|
||||||
|
Iterable<Location> locations) {
|
||||||
|
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
||||||
|
myMapsFeature.generateAndroidId();
|
||||||
|
myMapsFeature.setType(MyMapsFeature.LINE);
|
||||||
|
if (TextUtils.isEmpty(trackName)) {
|
||||||
|
// Features must have a name (otherwise GData upload may fail):
|
||||||
|
myMapsFeature.setTitle("-");
|
||||||
|
} else {
|
||||||
|
myMapsFeature.setTitle(trackName);
|
||||||
|
}
|
||||||
|
myMapsFeature.setColor(0x80FF0000);
|
||||||
|
for (Location loc : locations) {
|
||||||
|
myMapsFeature.addPoint(getGeoPoint(loc));
|
||||||
|
}
|
||||||
|
return myMapsFeature;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up after a series of uploads.
|
* Cleans up after a series of uploads.
|
||||||
* This closes the connection to Maps and resets retry counters.
|
* This closes the connection to Maps and resets retry counters.
|
||||||
*/
|
*/
|
||||||
void cleanUp();
|
public void cleanUp() {
|
||||||
|
wrapper.cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GeoPoint getGeoPoint(Location location) {
|
||||||
|
return new GeoPoint((int) (location.getLatitude() * 1E6),
|
||||||
|
(int) (location.getLongitude() * 1E6));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,338 +0,0 @@
|
|||||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
|
||||||
package com.google.android.apps.mytracks.io.mymaps;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
|
||||||
import com.google.android.apps.mytracks.io.AuthManager;
|
|
||||||
import com.google.android.maps.GeoPoint;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
import com.google.wireless.gdata.client.HttpException;
|
|
||||||
import com.google.wireless.gdata.data.Entry;
|
|
||||||
import com.google.wireless.gdata.parser.GDataParser;
|
|
||||||
import com.google.wireless.gdata.parser.ParseException;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.location.Location;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of the Maps access abstraction.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class MapsFacadeImpl implements MapsFacade {
|
|
||||||
private static final String END_ICON_URL =
|
|
||||||
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
|
||||||
private static final String START_ICON_URL =
|
|
||||||
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
private final MyMapsGDataWrapper wrapper;
|
|
||||||
private final MyMapsGDataConverter gdataConverter;
|
|
||||||
private final String authToken;
|
|
||||||
|
|
||||||
public MapsFacadeImpl(Context context, AuthManager auth) {
|
|
||||||
this.context = context;
|
|
||||||
this.authToken = auth.getAuthToken();
|
|
||||||
|
|
||||||
wrapper = new MyMapsGDataWrapper(context, auth);
|
|
||||||
wrapper.setRetryOnAuthFailure(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
gdataConverter = new MyMapsGDataConverter();
|
|
||||||
} catch (XmlPullParserException e) {
|
|
||||||
throw new IllegalStateException("Unable to create maps data converter", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String buildMapUrl(String mapId) {
|
|
||||||
return MapsClient.buildMapUrl(mapId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getMapsList(final MapsListCallback callback) {
|
|
||||||
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
|
||||||
@Override
|
|
||||||
public void query(MapsClient client) throws IOException, Exception {
|
|
||||||
GDataParser listParser = client.getParserForFeed(
|
|
||||||
MapFeatureEntry.class, MapsClient.getMapsFeed(), authToken);
|
|
||||||
listParser.init();
|
|
||||||
while (listParser.hasMoreData()) {
|
|
||||||
MapFeatureEntry entry =
|
|
||||||
(MapFeatureEntry) listParser.readNextEntry(null);
|
|
||||||
MyMapsMapMetadata metadata =
|
|
||||||
MyMapsGDataConverter.getMapMetadataForEntry(entry);
|
|
||||||
String mapId = MyMapsGDataConverter.getMapidForEntry(entry);
|
|
||||||
|
|
||||||
callback.onReceivedMapListing(
|
|
||||||
mapId, metadata.getTitle(), metadata.getDescription(), metadata.getSearchable());
|
|
||||||
}
|
|
||||||
listParser.close();
|
|
||||||
listParser = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new map for the given track.
|
|
||||||
*
|
|
||||||
* @param track The track that will be uploaded to this map
|
|
||||||
* @return True on success
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean createNewMap(
|
|
||||||
final String title, final String category, final String description,
|
|
||||||
final boolean isPublic, final StringBuilder mapIdBuilder) {
|
|
||||||
if (mapIdBuilder.length() > 0) {
|
|
||||||
throw new IllegalArgumentException("mapIdBuilder should be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
|
||||||
@Override
|
|
||||||
public void query(MapsClient client) throws IOException, Exception {
|
|
||||||
Log.d(MyMapsConstants.TAG, "Creating a new map.");
|
|
||||||
String mapFeed = MapsClient.getMapsFeed();
|
|
||||||
Log.d(MyMapsConstants.TAG, "Map feed is " + mapFeed);
|
|
||||||
MyMapsMapMetadata metaData = new MyMapsMapMetadata();
|
|
||||||
metaData.setTitle(title);
|
|
||||||
metaData.setDescription(description + " - "
|
|
||||||
+ category + " - " + context.getString(R.string.new_map_description));
|
|
||||||
metaData.setSearchable(isPublic);
|
|
||||||
Entry entry = MyMapsGDataConverter.getMapEntryForMetadata(metaData);
|
|
||||||
Log.d(MyMapsConstants.TAG, "Title: " + entry.getTitle());
|
|
||||||
Entry map = client.createEntry(mapFeed, authToken, entry);
|
|
||||||
|
|
||||||
String mapId = MapsClient.getMapIdFromMapEntryId(map.getId());
|
|
||||||
mapIdBuilder.append(mapId);
|
|
||||||
Log.d(MyMapsConstants.TAG, "New map id is: " + mapId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean uploadMarker(final String mapId, final String trackName,
|
|
||||||
final String trackDescription, final Location loc, final boolean isStart) {
|
|
||||||
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
|
||||||
@Override
|
|
||||||
public void query(MapsClient client)
|
|
||||||
throws IOException, Exception {
|
|
||||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
|
||||||
GeoPoint geoPoint = getGeoPoint(loc);
|
|
||||||
insertMarker(client, featureFeed, trackName, trackDescription, geoPoint, isStart);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a place mark. Second try if 1st try fails. Will throw exception on
|
|
||||||
* 2nd failure.
|
|
||||||
*/
|
|
||||||
private void insertMarker(MapsClient client,
|
|
||||||
String featureFeed,
|
|
||||||
String trackName, String trackDescription,
|
|
||||||
GeoPoint geoPoint,
|
|
||||||
boolean isStart) throws IOException, Exception {
|
|
||||||
MyMapsFeature feature =
|
|
||||||
buildMyMapsPlacemarkFeature(trackName, trackDescription, geoPoint, isStart);
|
|
||||||
Entry entry = gdataConverter.getEntryForFeature(feature);
|
|
||||||
Log.d(MyMapsConstants.TAG, "SendToMyMaps: Creating placemark "
|
|
||||||
+ entry.getTitle());
|
|
||||||
try {
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG, "SendToMyMaps: createEntry success!");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
|
||||||
// Retry once (often IOException is thrown on a timeout):
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry success on 2nd try!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a placemark MyMapsFeature from a track.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
* @param isStart true if it's the start of the track, or false for end
|
|
||||||
* @return a MyMapsFeature
|
|
||||||
*/
|
|
||||||
private MyMapsFeature buildMyMapsPlacemarkFeature(
|
|
||||||
String trackName, String trackDescription,
|
|
||||||
GeoPoint geoPoint, boolean isStart) {
|
|
||||||
String iconUrl;
|
|
||||||
if (isStart) {
|
|
||||||
iconUrl = START_ICON_URL;
|
|
||||||
} else {
|
|
||||||
iconUrl = END_ICON_URL;
|
|
||||||
}
|
|
||||||
String title = trackName + " "
|
|
||||||
+ (isStart ? context.getString(R.string.start)
|
|
||||||
: context.getString(R.string.end));
|
|
||||||
String description = isStart ? "" : trackDescription;
|
|
||||||
return buildMyMapsPlacemarkFeature(title, description, iconUrl, geoPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a MyMapsFeature from a track.
|
|
||||||
*
|
|
||||||
* @param wpt the waypoint
|
|
||||||
* @return a MyMapsFeature
|
|
||||||
*/
|
|
||||||
private static MyMapsFeature buildMyMapsPlacemarkFeature(
|
|
||||||
String title, String description, String iconUrl, GeoPoint geoPoint) {
|
|
||||||
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
|
||||||
myMapsFeature.generateAndroidId();
|
|
||||||
myMapsFeature.setType(MyMapsFeature.MARKER);
|
|
||||||
myMapsFeature.setIconUrl(iconUrl);
|
|
||||||
myMapsFeature.setDescription(description);
|
|
||||||
myMapsFeature.addPoint(geoPoint);
|
|
||||||
if (TextUtils.isEmpty(title)) {
|
|
||||||
// Features must have a name (otherwise GData upload may fail):
|
|
||||||
myMapsFeature.setTitle("-");
|
|
||||||
} else {
|
|
||||||
myMapsFeature.setTitle(title);
|
|
||||||
}
|
|
||||||
myMapsFeature.setDescription(description.replaceAll("\n", "<br>"));
|
|
||||||
return myMapsFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads all of the waypoints associated with this track to map.
|
|
||||||
*
|
|
||||||
* @param track The track to upload waypoints for
|
|
||||||
* @return True on success
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean uploadWaypoints(
|
|
||||||
final String mapId, final Iterable<Waypoint> waypoints) {
|
|
||||||
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
|
||||||
public void query(MapsClient client) {
|
|
||||||
// TODO(rdamazio): Stream through the waypoints in chunks.
|
|
||||||
// I am leaving the number of waypoints very high which should not be a
|
|
||||||
// problem because we don't try to load them into objects all at the
|
|
||||||
// same time.
|
|
||||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (Waypoint waypoint : waypoints) {
|
|
||||||
MyMapsFeature feature = buildMyMapsPlacemarkFeature(
|
|
||||||
waypoint.getName(), waypoint.getDescription(), waypoint.getIcon(),
|
|
||||||
getGeoPoint(waypoint.getLocation()));
|
|
||||||
Entry entry = gdataConverter.getEntryForFeature(feature);
|
|
||||||
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: Creating waypoint.");
|
|
||||||
try {
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry success!");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry 1st try failed. Retrying.");
|
|
||||||
|
|
||||||
// Retry once (often IOException is thrown on a timeout):
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry success on 2nd try!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ParseException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG, "ParseException caught.", e);
|
|
||||||
} catch (HttpException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG, "HttpException caught.", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG, "IOException caught.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads a given list of tracks to Google MyMaps using the maps GData feed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean uploadTrackPoints(
|
|
||||||
final String mapId, final String trackName, final Collection<Location> locations) {
|
|
||||||
return wrapper.runQuery(new MyMapsGDataWrapper.QueryFunction() {
|
|
||||||
@Override
|
|
||||||
public void query(MapsClient client)
|
|
||||||
throws IOException, Exception {
|
|
||||||
String featureFeed = MapsClient.getFeaturesFeed(mapId);
|
|
||||||
Log.d(MyMapsConstants.TAG, "Feature feed url: " + featureFeed);
|
|
||||||
uploadTrackPoints(client, featureFeed, trackName, locations);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean uploadTrackPoints(
|
|
||||||
MapsClient client,
|
|
||||||
String featureFeed,
|
|
||||||
String trackName, Collection<Location> locations)
|
|
||||||
throws IOException, Exception {
|
|
||||||
Entry entry = null;
|
|
||||||
int numLocations = locations.size();
|
|
||||||
if (numLocations < 2) {
|
|
||||||
// Need at least two points for a polyline:
|
|
||||||
Log.w(MyMapsConstants.TAG, "Not uploading too few points");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the line:
|
|
||||||
entry = gdataConverter.getEntryForFeature(
|
|
||||||
buildMyMapsLineFeature(trackName, locations));
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: Creating line " + entry.getTitle());
|
|
||||||
try {
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG, "SendToMyMaps: createEntry success!");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry 1st try failed. Trying again.");
|
|
||||||
// Retry once (often IOException is thrown on a timeout):
|
|
||||||
client.createEntry(featureFeed, authToken, entry);
|
|
||||||
Log.d(MyMapsConstants.TAG,
|
|
||||||
"SendToMyMaps: createEntry success on 2nd try!");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a MyMapsFeature from a track.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
* @return a MyMapsFeature
|
|
||||||
*/
|
|
||||||
private static MyMapsFeature buildMyMapsLineFeature(String trackName,
|
|
||||||
Iterable<Location> locations) {
|
|
||||||
MyMapsFeature myMapsFeature = new MyMapsFeature();
|
|
||||||
myMapsFeature.generateAndroidId();
|
|
||||||
myMapsFeature.setType(MyMapsFeature.LINE);
|
|
||||||
if (TextUtils.isEmpty(trackName)) {
|
|
||||||
// Features must have a name (otherwise GData upload may fail):
|
|
||||||
myMapsFeature.setTitle("-");
|
|
||||||
} else {
|
|
||||||
myMapsFeature.setTitle(trackName);
|
|
||||||
}
|
|
||||||
myMapsFeature.setColor(0x80FF0000);
|
|
||||||
for (Location loc : locations) {
|
|
||||||
myMapsFeature.addPoint(getGeoPoint(loc));
|
|
||||||
}
|
|
||||||
return myMapsFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cleanUp() {
|
|
||||||
wrapper.cleanUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GeoPoint getGeoPoint(Location location) {
|
|
||||||
return new GeoPoint((int) (location.getLatitude() * 1E6),
|
|
||||||
(int) (location.getLongitude() * 1E6));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user