From 86de9830b4787f3151429749ca1b5b15175c9d3b Mon Sep 17 00:00:00 2001 From: Rodrigo Damazio Date: Wed, 16 Mar 2011 13:06:53 -0700 Subject: [PATCH] Merging MapsFacadeImpl into MapsFacade --- .../android/apps/mytracks/MyMapsList.java | 5 +- .../android/apps/mytracks/MyTracks.java | 6 +- .../apps/mytracks/io/SendToMyMaps.java | 9 +- .../apps/mytracks/io/mymaps/MapsFacade.java | 307 +++++++++++++++- .../mytracks/io/mymaps/MapsFacadeImpl.java | 338 ------------------ 5 files changed, 305 insertions(+), 360 deletions(-) delete mode 100644 MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacadeImpl.java diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyMapsList.java b/MyTracks/src/com/google/android/apps/mytracks/MyMapsList.java index bcdbe11f8..7b74ac606 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyMapsList.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyMapsList.java @@ -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.AuthManagerFactory; 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.maps.mytracks.R; @@ -125,7 +124,7 @@ public class MyMapsList extends Activity implements MapsFacade.MapsListCallback auth.doLogin(new Runnable() { public void run() { // Runs in UI thread. - mapsClient = new MapsFacadeImpl(MyMapsList.this, auth); + mapsClient = new MapsFacade(MyMapsList.this, auth); startLookup(); } @@ -200,7 +199,7 @@ public class MyMapsList extends Activity implements MapsFacade.MapsListCallback shareIntent.putExtra(Intent.EXTRA_TEXT, String.format( getText(R.string.share_map_body_format).toString(), listItem[1], - MapsFacadeImpl.buildMapUrl(listItem[0]))); + MapsFacade.buildMapUrl(listItem[0]))); startActivity(Intent.createChooser(shareIntent, getText(R.string.share_map).toString())); return true; diff --git a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java index 3050efd94..64db86daf 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MyTracks.java @@ -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.TrackWriterFactory; 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.sendtogoogle.ResultDialogFactory; 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); if (selectedTrack != null) { if (!TextUtils.isEmpty(selectedTrack.getMapId())) { - shareLinkToMap(MapsFacadeImpl.buildMapUrl(selectedTrack.getMapId())); + shareLinkToMap(MapsFacade.buildMapUrl(selectedTrack.getMapId())); } else if (!TextUtils.isEmpty(selectedTrack.getTableId())) { shareLinkToMap(getFusionTablesUrl(selectedTrackId)); } else { @@ -1226,7 +1226,7 @@ public class MyTracks extends TabActivity implements OnTouchListener, String url = null; if (sentToMyMaps && sendToMyMapsSuccess) { // Prefer a link to My Maps - url = MapsFacadeImpl.buildMapUrl(sendToMyMapsMapId); + url = MapsFacade.buildMapUrl(sendToMyMapsMapId); } else if (sentToFusionTables && sendToFusionTablesSuccess) { // Otherwise try using the link to fusion tables url = getFusionTablesUrl(sendToTrackId); diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java index bff49362a..3891b0322 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/SendToMyMaps.java @@ -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.Waypoint; 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.TripStatistics; import com.google.android.apps.mytracks.util.MyTracksUtils; @@ -112,7 +110,7 @@ public class SendToMyMaps implements Runnable { track.setDescription("

" + track.getDescription() + "

" + stringUtils.generateTrackDescription(track, null, null) + "

"); - mapsClient = new MapsFacadeImpl(context, auth); + mapsClient = new MapsFacade(context, auth); // Create a new map if necessary: boolean isNewMap = mapId.equals(NEW_MAP_ID); @@ -149,11 +147,10 @@ public class SendToMyMaps implements Runnable { if (c.getCount() > 1 && c.moveToFirst()) { // This will skip the 1st waypoint (it carries the stats for the // last segment). - ArrayList waypoints = new ArrayList(c.getCount() - 1); + ArrayList waypoints = new ArrayList(c.getCount() - 1); while (c.moveToNext()) { Waypoint wpt = providerUtils.createWaypoint(c); - waypoints.add(new WaypointData( - wpt.getName(), wpt.getDescription(), wpt.getIcon(), wpt.getLocation())); + waypoints.add(wpt); } success = mapsClient.uploadWaypoints(mapId, waypoints); diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacade.java b/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacade.java index e2a353899..86a92ec5c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacade.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacade.java @@ -2,17 +2,30 @@ 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; + /** * Single interface which abstracts all access to the Google Maps service. * * @author Rodrigo Damazio */ -public interface MapsFacade { +public class MapsFacade { /** * Interface for receiving data back from getMapsList. @@ -23,13 +36,62 @@ public interface MapsFacade { 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. * * @param callback callback to call for each map returned * @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. @@ -41,8 +103,34 @@ public interface MapsFacade { * @param mapIdBuilder builder to append the map ID to * @return true on success, false otherwise */ - boolean createNewMap(String title, String category, String description, - boolean isPublic, StringBuilder mapIdBuilder); + 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); + } + }); + } /** * 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 * @return true on success, false otherwise */ - boolean uploadMarker(String mapId, String trackName, String trackDescription, - Location location, boolean isStart); + 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", "
")); + return myMapsFeature; + } /** * Uploads a series of waypoints to the given map. @@ -64,7 +236,49 @@ public interface MapsFacade { * @param waypoints the waypoints to upload * @return true on success, false otherwise */ - boolean uploadWaypoints(String mapId, Iterable waypoints); + public boolean uploadWaypoints( + final String mapId, final Iterable 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. @@ -74,12 +288,85 @@ public interface MapsFacade { * @param locations the locations to upload * @return true on success, false otherwise */ - boolean uploadTrackPoints(String mapId, String trackName, - Collection locations); + public boolean uploadTrackPoints( + final String mapId, final String trackName, final Collection 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 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 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. * 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)); + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacadeImpl.java b/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacadeImpl.java deleted file mode 100644 index 011ac04e2..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/io/mymaps/MapsFacadeImpl.java +++ /dev/null @@ -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", "
")); - 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 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 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 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 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)); - } -}