forked from upstream-mirrors/OpenTracks
Merging Matthew's change.
This commit is contained in:
@@ -5,4 +5,3 @@ syntax:glob
|
|||||||
*/proguard/**
|
*/proguard/**
|
||||||
.metadata
|
.metadata
|
||||||
MyTracksTest/annotations.log
|
MyTracksTest/annotations.log
|
||||||
MyTracks/src/com/google/android/apps/mytracks/io/mymaps
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<classpathentry kind="src" path="gen"/>
|
||||||
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
|
<classpathentry exported="true" kind="lib" path="/MyTracksLib/libs/protobuf-java-2.3.0-lite.jar"/>
|
||||||
<classpathentry kind="lib" path="libs/libmymaps.jar"/>
|
|
||||||
<classpathentry kind="src" path="MyTracksLib_src"/>
|
<classpathentry kind="src" path="MyTracksLib_src"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
Binary file not shown.
@@ -18,9 +18,8 @@ package com.google.android.apps.mytracks;
|
|||||||
import com.google.android.accounts.Account;
|
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.MyMapsFactory;
|
|
||||||
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.MapsService;
|
import com.google.android.apps.mytracks.io.mymaps.MyMapsConstants;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -84,7 +83,7 @@ public class MyMapsList extends Activity implements MapsFacade.MapsListCallback
|
|||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
|
|
||||||
auth = AuthManagerFactory.getAuthManager(this, GET_LOGIN, null, true,
|
auth = AuthManagerFactory.getAuthManager(this, GET_LOGIN, null, true,
|
||||||
MapsService.getServiceName());
|
MyMapsConstants.SERVICE_NAME);
|
||||||
|
|
||||||
setContentView(R.layout.list);
|
setContentView(R.layout.list);
|
||||||
|
|
||||||
@@ -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 = MyMapsFactory.newMapsClient(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],
|
||||||
MapsService.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,8 @@ 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.MapsService;
|
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.ResultDialogFactory;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendResult;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendResult;
|
||||||
@@ -606,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(MapsService.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 {
|
||||||
@@ -1028,7 +1029,7 @@ public class MyTracks extends TabActivity implements OnTouchListener,
|
|||||||
setProgressMessage(
|
setProgressMessage(
|
||||||
R.string.progress_message_authenticating_mymaps);
|
R.string.progress_message_authenticating_mymaps);
|
||||||
authenticate(results, MyTracksConstants.AUTHENTICATE_TO_MY_MAPS,
|
authenticate(results, MyTracksConstants.AUTHENTICATE_TO_MY_MAPS,
|
||||||
MapsService.getServiceName());
|
MyMapsConstants.SERVICE_NAME);
|
||||||
// AUTHENTICATE_TO_MY_MAPS callback calls sendToGoogleMaps
|
// AUTHENTICATE_TO_MY_MAPS callback calls sendToGoogleMaps
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1225,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 = MapsService.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);
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
package com.google.android.apps.mytracks.io;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacade;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsService;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsStringsProvider;
|
|
||||||
import com.google.android.apps.mytracks.io.mymaps.MapsFacade.AuthenticationRefresher;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory to easily instantiate a {@link MapsFacade}.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class MyMapsFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple provider for reading strings from resources.
|
|
||||||
*/
|
|
||||||
private static class MyMapsStringProvider implements MapsStringsProvider {
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public MyMapsStringProvider(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNewMapDescription() {
|
|
||||||
return context.getString(R.string.new_map_description);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getStart() {
|
|
||||||
return context.getString(R.string.start);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEnd() {
|
|
||||||
return context.getString(R.string.end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Maps fa�ade interface instance.
|
|
||||||
*
|
|
||||||
* @param context current context
|
|
||||||
* @param auth a valid authentication manager
|
|
||||||
* @return the fa�ade
|
|
||||||
*/
|
|
||||||
public static MapsFacade newMapsClient(Activity context, final AuthManager auth) {
|
|
||||||
MapsFacade client = MapsService.newClient(context,
|
|
||||||
GDataClientFactory.getGDataClient(context),
|
|
||||||
new MyMapsStringProvider(context),
|
|
||||||
auth.getAuthToken());
|
|
||||||
client.setAuthenticationRefresher(new AuthenticationRefresher() {
|
|
||||||
@Override
|
|
||||||
public void invalidateAndRefresh(Runnable done) {
|
|
||||||
auth.invalidateAndRefresh(done);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MyMapsFactory() {}
|
|
||||||
}
|
|
||||||
@@ -22,7 +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.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;
|
||||||
@@ -111,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 = MyMapsFactory.newMapsClient(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);
|
||||||
@@ -148,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);
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.android.maps.GeoPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rectangle in geographical space.
|
||||||
|
*/
|
||||||
|
class GeoRect {
|
||||||
|
public int top;
|
||||||
|
public int left;
|
||||||
|
public int bottom;
|
||||||
|
public int right;
|
||||||
|
|
||||||
|
public GeoRect() {
|
||||||
|
top = 0;
|
||||||
|
left = 0;
|
||||||
|
bottom = 0;
|
||||||
|
right = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoRect(GeoPoint center, int latSpan, int longSpan) {
|
||||||
|
top = center.getLatitudeE6() - latSpan / 2;
|
||||||
|
left = center.getLongitudeE6() - longSpan / 2;
|
||||||
|
bottom = center.getLatitudeE6() + latSpan / 2;
|
||||||
|
right = center.getLongitudeE6() + longSpan / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoPoint getCenter() {
|
||||||
|
return new GeoPoint(top / 2 + bottom / 2, left / 2 + right / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLatSpan() {
|
||||||
|
return bottom - top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLongSpan() {
|
||||||
|
return right - left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(GeoPoint geoPoint) {
|
||||||
|
if (geoPoint.getLatitudeE6() >= top
|
||||||
|
&& geoPoint.getLatitudeE6() <= bottom
|
||||||
|
&& geoPoint.getLongitudeE6() >= left
|
||||||
|
&& geoPoint.getLongitudeE6() <= right) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GData entry for a map feature.
|
||||||
|
*/
|
||||||
|
class MapFeatureEntry extends Entry {
|
||||||
|
|
||||||
|
private String mPrivacy = null;
|
||||||
|
private Map<String, String> mAttributes = new HashMap<String, String>();
|
||||||
|
|
||||||
|
public void setPrivacy(String privacy) {
|
||||||
|
mPrivacy = privacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivacy() {
|
||||||
|
return mPrivacy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttribute(String name, String value) {
|
||||||
|
mAttributes.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
mAttributes.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAllAttributes() {
|
||||||
|
return mAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.client.GDataClient;
|
||||||
|
import com.google.wireless.gdata.client.GDataParserFactory;
|
||||||
|
import com.google.wireless.gdata.client.GDataServiceClient;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client to talk to Google Maps via GData.
|
||||||
|
*/
|
||||||
|
class MapsClient extends GDataServiceClient {
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
public static final boolean LOG_COMMUNICATION = false;
|
||||||
|
|
||||||
|
private static final String MAPS_BASE_FEED_URL =
|
||||||
|
"http://maps.google.com/maps/feeds/";
|
||||||
|
private static final String MAPS_MAP_FEED_PATH = "maps/default/full";
|
||||||
|
private static final String MAPS_FEATURE_FEED_PATH_BEFORE_MAPID = "features/";
|
||||||
|
private static final String MAPS_FEATURE_FEED_PATH_AFTER_MAPID = "/full";
|
||||||
|
private static final String MAPS_VERSION_FEED_PATH_FORMAT =
|
||||||
|
"%smaps/%s/versions/%s/full/%s";
|
||||||
|
|
||||||
|
private static final String MAP_ENTRY_ID_BEFORE_USER_ID = "maps/feeds/maps/";
|
||||||
|
private static final String MAP_ENTRY_ID_BETWEEN_USER_ID_AND_MAP_ID = "/";
|
||||||
|
private static final String V2_ONLY_PARAM = "?v=2.0";
|
||||||
|
|
||||||
|
public MapsClient(GDataClient dataClient,
|
||||||
|
GDataParserFactory dataParserFactory) {
|
||||||
|
super(dataClient, dataParserFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceName() {
|
||||||
|
return MyMapsConstants.SERVICE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildMapUrl(String mapId) {
|
||||||
|
return MyMapsConstants.MAPSHOP_BASE_URL + "?msa=0&msid=" + mapId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMapsFeed() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d("Maps Client", "Requesting map feed:");
|
||||||
|
}
|
||||||
|
return MAPS_BASE_FEED_URL + MAPS_MAP_FEED_PATH + V2_ONLY_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFeaturesFeed(String mapid) {
|
||||||
|
StringBuilder feed = new StringBuilder();
|
||||||
|
feed.append(MAPS_BASE_FEED_URL);
|
||||||
|
feed.append(MAPS_FEATURE_FEED_PATH_BEFORE_MAPID);
|
||||||
|
feed.append(mapid);
|
||||||
|
feed.append(MAPS_FEATURE_FEED_PATH_AFTER_MAPID);
|
||||||
|
feed.append(V2_ONLY_PARAM);
|
||||||
|
return feed.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMapIdFromMapEntryId(String entryId) {
|
||||||
|
String userId = null;
|
||||||
|
String mapId = null;
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d("Maps GData Client", "Getting mapid from entry id: " + entryId);
|
||||||
|
}
|
||||||
|
int userIdStart =
|
||||||
|
entryId.indexOf(MAP_ENTRY_ID_BEFORE_USER_ID)
|
||||||
|
+ MAP_ENTRY_ID_BEFORE_USER_ID.length();
|
||||||
|
int userIdEnd =
|
||||||
|
entryId.indexOf(MAP_ENTRY_ID_BETWEEN_USER_ID_AND_MAP_ID, userIdStart);
|
||||||
|
if (userIdStart >= 0 && userIdEnd < entryId.length()
|
||||||
|
&& userIdStart <= userIdEnd) {
|
||||||
|
userId = entryId.substring(userIdStart, userIdEnd);
|
||||||
|
}
|
||||||
|
int mapIdStart =
|
||||||
|
entryId.indexOf(MAP_ENTRY_ID_BETWEEN_USER_ID_AND_MAP_ID, userIdEnd)
|
||||||
|
+ MAP_ENTRY_ID_BETWEEN_USER_ID_AND_MAP_ID.length();
|
||||||
|
if (mapIdStart >= 0 && mapIdStart < entryId.length()) {
|
||||||
|
mapId = entryId.substring(mapIdStart);
|
||||||
|
}
|
||||||
|
if (userId == null) {
|
||||||
|
userId = "";
|
||||||
|
}
|
||||||
|
if (mapId == null) {
|
||||||
|
mapId = "";
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d("Maps GData Client", "Got user id: " + userId);
|
||||||
|
Log.d("Maps GData Client", "Got map id: " + mapId);
|
||||||
|
}
|
||||||
|
return userId + "." + mapId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getVersionFeed(String versionUserId,
|
||||||
|
String versionClient, String currentVersion) {
|
||||||
|
return String.format(MAPS_VERSION_FEED_PATH_FORMAT,
|
||||||
|
MAPS_BASE_FEED_URL, versionUserId,
|
||||||
|
versionClient, currentVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single interface which abstracts all access to the Google Maps service.
|
||||||
|
*
|
||||||
|
* @author Rodrigo Damazio
|
||||||
|
*/
|
||||||
|
public class MapsFacade {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for receiving data back from getMapsList.
|
||||||
|
* All calls to the interface will happen before getMapsList returns.
|
||||||
|
*/
|
||||||
|
public interface MapsListCallback {
|
||||||
|
void onReceivedMapListing(String mapId, String title, String description,
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param title title of the map
|
||||||
|
* @param category category of the map
|
||||||
|
* @param description description for the map
|
||||||
|
* @param isPublic whether the map should be public
|
||||||
|
* @param mapIdBuilder builder to append the map ID to
|
||||||
|
* @return true on success, false otherwise
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param mapId ID of the map to upload to
|
||||||
|
* @param trackName name of the track being started/ended
|
||||||
|
* @param trackDescription description of the track being started/ended
|
||||||
|
* @param location the location of the marker
|
||||||
|
* @param isStart true to add a start marker, false to add an end marker
|
||||||
|
* @return true on success, false otherwise
|
||||||
|
*/
|
||||||
|
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 a series of waypoints to the given map.
|
||||||
|
*
|
||||||
|
* @param mapId ID of the map to upload to
|
||||||
|
* @param waypoints the waypoints to upload
|
||||||
|
* @return true on success, false otherwise
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param mapId ID of the map to upload to
|
||||||
|
* @param trackName the name of the track
|
||||||
|
* @param locations the locations to upload
|
||||||
|
* @return true on success, false otherwise
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up after a series of uploads.
|
||||||
|
* This closes the connection to Maps and resets retry counters.
|
||||||
|
*/
|
||||||
|
public void cleanUp() {
|
||||||
|
wrapper.cleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GeoPoint getGeoPoint(Location location) {
|
||||||
|
return new GeoPoint((int) (location.getLatitude() * 1E6),
|
||||||
|
(int) (location.getLongitude() * 1E6));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for My Maps.
|
||||||
|
*/
|
||||||
|
public class MyMapsConstants {
|
||||||
|
static final String TAG = "MapsApi";
|
||||||
|
static final String MAPSHOP_BASE_URL =
|
||||||
|
"http://maps.google.com/maps/ms";
|
||||||
|
public static final String SERVICE_NAME = "local";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor to prevent instantiation.
|
||||||
|
*/
|
||||||
|
private MyMapsConstants() { }
|
||||||
|
}
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.android.maps.GeoPoint;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyMapsFeature contains all of the data associated with a feature in My Maps,
|
||||||
|
* where a feature is a marker, line, or shape. Some of the data is stored in a
|
||||||
|
* {@link MyMapsFeatureMetadata} object so that it can be more efficiently
|
||||||
|
* transmitted to other activities.
|
||||||
|
*/
|
||||||
|
class MyMapsFeature implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 8439035544430497236L;
|
||||||
|
|
||||||
|
/** A marker feature displays an icon at a single point on the map. */
|
||||||
|
public static final int MARKER = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A line feature displays a line connecting a set of points on the map.
|
||||||
|
*/
|
||||||
|
public static final int LINE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shape feature displays a border defined by connecting a set of points,
|
||||||
|
* including connecting the last to the first, and displays the area
|
||||||
|
* confined by this border.
|
||||||
|
*/
|
||||||
|
public static final int SHAPE = 2;
|
||||||
|
|
||||||
|
/** The local feature id for this feature, if needed. */
|
||||||
|
private String androidId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The latitudes of the points of this feature in order, specified in
|
||||||
|
* millionths of a degree north.
|
||||||
|
*/
|
||||||
|
private final Vector<Integer> latitudeE6 = new Vector<Integer>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The longitudes of the points of this feature in order, specified in
|
||||||
|
* millionths of a degree east.
|
||||||
|
*/
|
||||||
|
private final Vector<Integer> longitudeE6 = new Vector<Integer>();
|
||||||
|
|
||||||
|
/** The metadata of this feature in a format efficient for transmission. */
|
||||||
|
private MyMapsFeatureMetadata featureInfo = new MyMapsFeatureMetadata();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a valid but empty feature. It will default to a
|
||||||
|
* {@link #MARKER} with a blue placemark with a dot as an icon at the
|
||||||
|
* location (0, 0).
|
||||||
|
*/
|
||||||
|
public MyMapsFeature() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new point to the end of this feature.
|
||||||
|
*
|
||||||
|
* @param point The new point to add
|
||||||
|
*/
|
||||||
|
public synchronized void addPoint(GeoPoint point) {
|
||||||
|
latitudeE6.add(point.getLatitudeE6());
|
||||||
|
longitudeE6.add(point.getLongitudeE6());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new local id for this feature based on the current time and
|
||||||
|
* a random number.
|
||||||
|
*/
|
||||||
|
public synchronized void generateAndroidId() {
|
||||||
|
Long time = System.currentTimeMillis();
|
||||||
|
Integer random = (int) (Math.random() * 10000);
|
||||||
|
androidId = time.toString() + "." + random.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current local id for this feature if one is available.
|
||||||
|
*
|
||||||
|
* @return The local id for this feature
|
||||||
|
*/
|
||||||
|
public String getAndroidId() {
|
||||||
|
return androidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current (html) description of this feature. The description
|
||||||
|
* is stored in the feature metadata.
|
||||||
|
*
|
||||||
|
* @return The description of this feature
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return featureInfo.getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the description of this feature. That description is stored in the
|
||||||
|
* feature metadata.
|
||||||
|
*
|
||||||
|
* @param description The new description of this feature
|
||||||
|
*/
|
||||||
|
public synchronized void setDescription(String description) {
|
||||||
|
featureInfo.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the point at the given index for this feature.
|
||||||
|
*
|
||||||
|
* @param index The index of the point desired
|
||||||
|
* @return A {@link GeoPoint} representing the point or null if that point
|
||||||
|
* doesn't exist
|
||||||
|
*/
|
||||||
|
public GeoPoint getPoint(int index) {
|
||||||
|
if (latitudeE6.size() <= index) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new GeoPoint(latitudeE6.get(index), longitudeE6.get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of points in this feature and return that count.
|
||||||
|
*
|
||||||
|
* @return The number of points in this feature
|
||||||
|
*/
|
||||||
|
public int getPointCount() {
|
||||||
|
return latitudeE6.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the title of this feature. That title is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @return the current title of this feature
|
||||||
|
*/
|
||||||
|
public String getTitle() {
|
||||||
|
return featureInfo.getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the type of this feature. That type is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @return One of {@link #MARKER}, {@link #LINE}, or {@link #SHAPE}
|
||||||
|
* identifying the type of this feature
|
||||||
|
*/
|
||||||
|
public int getType() {
|
||||||
|
return featureInfo.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current color of this feature as an ARGB color integer.
|
||||||
|
* That color is stored in the feature metadata.
|
||||||
|
*
|
||||||
|
* @return The ARGB color of this feature
|
||||||
|
*/
|
||||||
|
public int getColor() {
|
||||||
|
return featureInfo.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current line width of this feature. That line width is
|
||||||
|
* stored in the feature metadata.
|
||||||
|
*
|
||||||
|
* @return The line width of this feature
|
||||||
|
*/
|
||||||
|
public int getLineWidth() {
|
||||||
|
return featureInfo.getLineWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current fill color of this feature as an ARGB color
|
||||||
|
* integer. That color is stored in the feature metadata.
|
||||||
|
*
|
||||||
|
* @return The ARGB fill color of this feature
|
||||||
|
*/
|
||||||
|
public int getFillColor() {
|
||||||
|
return featureInfo.getFillColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current icon url of this feature. That icon url is stored
|
||||||
|
* in the feature metadata.
|
||||||
|
*
|
||||||
|
* @return The icon url for this feature
|
||||||
|
*/
|
||||||
|
public String getIconUrl() {
|
||||||
|
return featureInfo.getIconUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the title of this feature. That title is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param title The new title of this feature
|
||||||
|
*/
|
||||||
|
public synchronized void setTitle(String title) {
|
||||||
|
featureInfo.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type of this feature. That type is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param type The new type of the feature. That type must be one of
|
||||||
|
* {@link #MARKER}, {@link #LINE}, or {@link #SHAPE}
|
||||||
|
*/
|
||||||
|
public synchronized void setType(int type) {
|
||||||
|
featureInfo.setType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ARGB color of this feature. That color is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param color The new ARGB color of this feature
|
||||||
|
*/
|
||||||
|
public synchronized void setColor(int color) {
|
||||||
|
featureInfo.setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the icon url of this feature. That icon url is stored in the feature
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* @param url The new icon url of the feature
|
||||||
|
*/
|
||||||
|
public synchronized void setIconUrl(String url) {
|
||||||
|
featureInfo.setIconUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a maps feature.
|
||||||
|
*/
|
||||||
|
class MyMapsFeatureMetadata {
|
||||||
|
|
||||||
|
private static final String BLUE_DOT_URL =
|
||||||
|
"http://maps.google.com/mapfiles/ms/micons/blue-dot.png";
|
||||||
|
private static final int DEFAULT_COLOR = 0x800000FF;
|
||||||
|
private static final int DEFAULT_FILL_COLOR = 0xC00000FF;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String description;
|
||||||
|
private int type;
|
||||||
|
private int color;
|
||||||
|
private int lineWidth;
|
||||||
|
private int fillColor;
|
||||||
|
private String iconUrl;
|
||||||
|
|
||||||
|
public MyMapsFeatureMetadata() {
|
||||||
|
title = "";
|
||||||
|
description = "";
|
||||||
|
type = MyMapsFeature.MARKER;
|
||||||
|
color = DEFAULT_COLOR;
|
||||||
|
lineWidth = 5;
|
||||||
|
fillColor = DEFAULT_FILL_COLOR;
|
||||||
|
iconUrl = BLUE_DOT_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(int type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color) {
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLineWidth() {
|
||||||
|
return lineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLineWidth(int width) {
|
||||||
|
lineWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFillColor() {
|
||||||
|
return fillColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFillColor(int color) {
|
||||||
|
fillColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIconUrl() {
|
||||||
|
return iconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIconUrl(String url) {
|
||||||
|
iconUrl = new String(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
import com.google.wireless.gdata.data.StringUtils;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlPullParserFactory;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converter from GData objects to MyMaps objects.
|
||||||
|
*/
|
||||||
|
class MyMapsGDataConverter {
|
||||||
|
|
||||||
|
private final XmlSerializer xmlSerializer;
|
||||||
|
|
||||||
|
public MyMapsGDataConverter() throws XmlPullParserException {
|
||||||
|
xmlSerializer = XmlPullParserFactory.newInstance().newSerializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MyMapsMapMetadata getMapMetadataForEntry(
|
||||||
|
MapFeatureEntry entry) {
|
||||||
|
MyMapsMapMetadata metadata = new MyMapsMapMetadata();
|
||||||
|
if ("public".equals(entry.getPrivacy())) {
|
||||||
|
metadata.setSearchable(true);
|
||||||
|
} else {
|
||||||
|
metadata.setSearchable(false);
|
||||||
|
}
|
||||||
|
metadata.setTitle(entry.getTitle());
|
||||||
|
|
||||||
|
String desc = entry.getContent();
|
||||||
|
if (desc.length() > 12) {
|
||||||
|
metadata.setDescription(desc.substring(9, desc.length() - 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
String editUri = entry.getEditUri();
|
||||||
|
if (editUri != null) {
|
||||||
|
metadata.setGDataEditUri(editUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMapidForEntry(Entry entry) {
|
||||||
|
return MapsClient.getMapIdFromMapEntryId(entry.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Entry getMapEntryForMetadata(MyMapsMapMetadata metadata) {
|
||||||
|
MapFeatureEntry entry = new MapFeatureEntry();
|
||||||
|
entry.setEditUri(metadata.getGDataEditUri());
|
||||||
|
entry.setTitle(metadata.getTitle());
|
||||||
|
entry.setContent(metadata.getDescription());
|
||||||
|
entry.setPrivacy(metadata.getSearchable() ? "public" : "unlisted");
|
||||||
|
entry.setAuthor("android");
|
||||||
|
entry.setEmail("nobody@google.com");
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapFeatureEntry getEntryForFeature(MyMapsFeature feature) {
|
||||||
|
MapFeatureEntry entry = new MapFeatureEntry();
|
||||||
|
entry.setTitle(feature.getTitle());
|
||||||
|
entry.setAuthor("android");
|
||||||
|
entry.setEmail("nobody@google.com");
|
||||||
|
entry.setCategoryScheme("http://schemas.google.com/g/2005#kind");
|
||||||
|
entry.setCategory("http://schemas.google.com/g/2008#mapfeature");
|
||||||
|
entry.setEditUri("");
|
||||||
|
if (!StringUtils.isEmpty(feature.getAndroidId())) {
|
||||||
|
entry.setAttribute("_androidId", feature.getAndroidId());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
xmlSerializer.setOutput(writer);
|
||||||
|
xmlSerializer.startTag(null, "Placemark");
|
||||||
|
xmlSerializer.attribute(null, "xmlns", "http://earth.google.com/kml/2.2");
|
||||||
|
xmlSerializer.startTag(null, "Style");
|
||||||
|
if (feature.getType() == MyMapsFeature.MARKER) {
|
||||||
|
xmlSerializer.startTag(null, "IconStyle");
|
||||||
|
xmlSerializer.startTag(null, "Icon");
|
||||||
|
xmlSerializer.startTag(null, "href");
|
||||||
|
xmlSerializer.text(feature.getIconUrl());
|
||||||
|
xmlSerializer.endTag(null, "href");
|
||||||
|
xmlSerializer.endTag(null, "Icon");
|
||||||
|
xmlSerializer.endTag(null, "IconStyle");
|
||||||
|
} else {
|
||||||
|
xmlSerializer.startTag(null, "LineStyle");
|
||||||
|
xmlSerializer.startTag(null, "color");
|
||||||
|
int color = feature.getColor();
|
||||||
|
// Reverse the color because KML is ABGR and Android is ARGB
|
||||||
|
xmlSerializer.text(Integer.toHexString(
|
||||||
|
Color.argb(Color.alpha(color), Color.blue(color),
|
||||||
|
Color.green(color), Color.red(color))));
|
||||||
|
xmlSerializer.endTag(null, "color");
|
||||||
|
xmlSerializer.startTag(null, "width");
|
||||||
|
xmlSerializer.text(Integer.toString(feature.getLineWidth()));
|
||||||
|
xmlSerializer.endTag(null, "width");
|
||||||
|
xmlSerializer.endTag(null, "LineStyle");
|
||||||
|
|
||||||
|
if (feature.getType() == MyMapsFeature.SHAPE) {
|
||||||
|
xmlSerializer.startTag(null, "PolyStyle");
|
||||||
|
xmlSerializer.startTag(null, "color");
|
||||||
|
int fcolor = feature.getFillColor();
|
||||||
|
// Reverse the color because KML is ABGR and Android is ARGB
|
||||||
|
xmlSerializer.text(Integer.toHexString(Color.argb(Color.alpha(fcolor),
|
||||||
|
Color.blue(fcolor), Color.green(fcolor), Color.red(fcolor))));
|
||||||
|
xmlSerializer.endTag(null, "color");
|
||||||
|
xmlSerializer.startTag(null, "fill");
|
||||||
|
xmlSerializer.text("1");
|
||||||
|
xmlSerializer.endTag(null, "fill");
|
||||||
|
xmlSerializer.startTag(null, "outline");
|
||||||
|
xmlSerializer.text("1");
|
||||||
|
xmlSerializer.endTag(null, "outline");
|
||||||
|
xmlSerializer.endTag(null, "PolyStyle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlSerializer.endTag(null, "Style");
|
||||||
|
xmlSerializer.startTag(null, "name");
|
||||||
|
xmlSerializer.text(feature.getTitle());
|
||||||
|
xmlSerializer.endTag(null, "name");
|
||||||
|
xmlSerializer.startTag(null, "description");
|
||||||
|
xmlSerializer.cdsect(feature.getDescription());
|
||||||
|
xmlSerializer.endTag(null, "description");
|
||||||
|
StringBuilder pointBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < feature.getPointCount(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
pointBuilder.append('\n');
|
||||||
|
}
|
||||||
|
pointBuilder.append(feature.getPoint(i).getLongitudeE6() / 1e6);
|
||||||
|
pointBuilder.append(',');
|
||||||
|
pointBuilder.append(feature.getPoint(i).getLatitudeE6() / 1e6);
|
||||||
|
pointBuilder.append(",0.000000");
|
||||||
|
}
|
||||||
|
String pointString = pointBuilder.toString();
|
||||||
|
if (feature.getType() == MyMapsFeature.MARKER) {
|
||||||
|
xmlSerializer.startTag(null, "Point");
|
||||||
|
xmlSerializer.startTag(null, "coordinates");
|
||||||
|
xmlSerializer.text(pointString);
|
||||||
|
xmlSerializer.endTag(null, "coordinates");
|
||||||
|
xmlSerializer.endTag(null, "Point");
|
||||||
|
} else if (feature.getType() == MyMapsFeature.LINE) {
|
||||||
|
xmlSerializer.startTag(null, "LineString");
|
||||||
|
xmlSerializer.startTag(null, "tessellate");
|
||||||
|
xmlSerializer.text("1");
|
||||||
|
xmlSerializer.endTag(null, "tessellate");
|
||||||
|
xmlSerializer.startTag(null, "coordinates");
|
||||||
|
xmlSerializer.text(pointString);
|
||||||
|
xmlSerializer.endTag(null, "coordinates");
|
||||||
|
xmlSerializer.endTag(null, "LineString");
|
||||||
|
} else {
|
||||||
|
xmlSerializer.startTag(null, "Polygon");
|
||||||
|
xmlSerializer.startTag(null, "outerBoundaryIs");
|
||||||
|
xmlSerializer.startTag(null, "LinearRing");
|
||||||
|
xmlSerializer.startTag(null, "tessellate");
|
||||||
|
xmlSerializer.text("1");
|
||||||
|
xmlSerializer.endTag(null, "tessellate");
|
||||||
|
xmlSerializer.startTag(null, "coordinates");
|
||||||
|
xmlSerializer.text(pointString + "\n"
|
||||||
|
+ Double.toString(feature.getPoint(0).getLongitudeE6() / 1e6)
|
||||||
|
+ ","
|
||||||
|
+ Double.toString(feature.getPoint(0).getLatitudeE6() / 1e6)
|
||||||
|
+ ",0.000000");
|
||||||
|
xmlSerializer.endTag(null, "coordinates");
|
||||||
|
xmlSerializer.endTag(null, "LinearRing");
|
||||||
|
xmlSerializer.endTag(null, "outerBoundaryIs");
|
||||||
|
xmlSerializer.endTag(null, "Polygon");
|
||||||
|
}
|
||||||
|
xmlSerializer.endTag(null, "Placemark");
|
||||||
|
xmlSerializer.flush();
|
||||||
|
entry.setContent(writer.toString());
|
||||||
|
Log.d("My Google Maps", "Generated kml:\n" + entry.getContent());
|
||||||
|
Log.d("My Google Maps", "Edit URI: " + entry.getEditUri());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.io.AuthManager;
|
||||||
|
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||||
|
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||||
|
import com.google.wireless.gdata.client.GDataClient;
|
||||||
|
import com.google.wireless.gdata.client.HttpException;
|
||||||
|
import com.google.wireless.gdata.parser.xml.SimplePullParser.ParseException;
|
||||||
|
import com.google.wireless.gdata2.ConflictDetectedException;
|
||||||
|
import com.google.wireless.gdata2.client.AuthenticationException;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyMapsGDataWrapper provides a wrapper around GData operations that maintains
|
||||||
|
* the GData client, and provides a method to run gdata queries with proper
|
||||||
|
* error handling. After a query is run, the wrapper can be queried about the
|
||||||
|
* error that occurred.
|
||||||
|
*/
|
||||||
|
class MyMapsGDataWrapper {
|
||||||
|
/**
|
||||||
|
* A QueryFunction is passed in when executing a query. The query function
|
||||||
|
* of the class is called with the GData client as a parameter. The
|
||||||
|
* function should execute whatever operations it desires on the client
|
||||||
|
* without concern for whether the client will throw an error.
|
||||||
|
*/
|
||||||
|
interface QueryFunction {
|
||||||
|
public abstract void query(MapsClient client)
|
||||||
|
throws AuthenticationException, IOException, ParseException,
|
||||||
|
ConflictDetectedException, Exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The types of error that may be encountered
|
||||||
|
/** No error occurred. */
|
||||||
|
public static final int ERROR_NO_ERROR = 0;
|
||||||
|
/** There was an authentication error, the auth token may be invalid. */
|
||||||
|
public static final int ERROR_AUTH = 1;
|
||||||
|
/** There was an internal error on the server side. */
|
||||||
|
public static final int ERROR_INTERNAL = 2;
|
||||||
|
/** There was an error connecting to the server. */
|
||||||
|
public static final int ERROR_CONNECTION = 3;
|
||||||
|
/** The item queried did not exit. */
|
||||||
|
public static final int ERROR_NOT_FOUND = 4;
|
||||||
|
/** There was an error parsing or serializing locally. */
|
||||||
|
public static final int ERROR_LOCAL = 5;
|
||||||
|
/** There was a conflict, update the entry and try again. */
|
||||||
|
public static final int ERROR_CONFLICT = 6;
|
||||||
|
/**
|
||||||
|
* A query was run after cleaning up the wrapper, so the client was invalid.
|
||||||
|
*/
|
||||||
|
public static final int ERROR_CLEANED_UP = 7;
|
||||||
|
/** An unknown error occurred. */
|
||||||
|
public static final int ERROR_UNKNOWN = 100;
|
||||||
|
|
||||||
|
private final GDataClient androidGdataClient;
|
||||||
|
private final AuthManager auth;
|
||||||
|
private final MapsClient client;
|
||||||
|
|
||||||
|
private String errorMessage;
|
||||||
|
private int errorType;
|
||||||
|
private boolean retryOnAuthFailure;
|
||||||
|
private int retriesPending;
|
||||||
|
private boolean cleanupCalled;
|
||||||
|
|
||||||
|
public MyMapsGDataWrapper(Context context, AuthManager auth) {
|
||||||
|
androidGdataClient = GDataClientFactory.getGDataClient(context);
|
||||||
|
this.auth = auth;
|
||||||
|
client =
|
||||||
|
new MapsClient(androidGdataClient, new XmlMapsGDataParserFactory(
|
||||||
|
new AndroidXmlParserFactory()));
|
||||||
|
errorType = ERROR_NO_ERROR;
|
||||||
|
errorMessage = null;
|
||||||
|
retryOnAuthFailure = false;
|
||||||
|
retriesPending = 0;
|
||||||
|
cleanupCalled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean runQuery(final QueryFunction query) {
|
||||||
|
if (client == null) {
|
||||||
|
errorType = ERROR_CLEANED_UP;
|
||||||
|
errorMessage = "GData Wrapper has already been cleaned up!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
query.query(client);
|
||||||
|
errorType = ERROR_NO_ERROR;
|
||||||
|
errorMessage = null;
|
||||||
|
return true;
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "Exception", e);
|
||||||
|
errorType = ERROR_AUTH;
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
} catch (HttpException e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "HttpException", e);
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
if (errorMessage != null && errorMessage.contains("401")) {
|
||||||
|
errorType = ERROR_AUTH;
|
||||||
|
} else {
|
||||||
|
errorType = ERROR_CONNECTION;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "Exception", e);
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
if (errorMessage != null && errorMessage.contains("503")) {
|
||||||
|
errorType = ERROR_INTERNAL;
|
||||||
|
} else {
|
||||||
|
errorType = ERROR_CONNECTION;
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "Exception", e);
|
||||||
|
errorType = ERROR_LOCAL;
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
} catch (ConflictDetectedException e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "Exception", e);
|
||||||
|
errorType = ERROR_CONFLICT;
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(MyMapsConstants.TAG, "Exception", e);
|
||||||
|
errorType = ERROR_UNKNOWN;
|
||||||
|
errorMessage = e.getMessage();
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Log.d(MyMapsConstants.TAG, "GData error encountered: " + errorMessage);
|
||||||
|
if (errorType == ERROR_AUTH && auth != null) {
|
||||||
|
Runnable whenFinished = null;
|
||||||
|
if (retryOnAuthFailure) {
|
||||||
|
retriesPending++;
|
||||||
|
whenFinished = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
retriesPending--;
|
||||||
|
retryOnAuthFailure = false;
|
||||||
|
runQuery(query);
|
||||||
|
if (cleanupCalled && retriesPending == 0) {
|
||||||
|
cleanUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
auth.invalidateAndRefresh(whenFinished);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorType() {
|
||||||
|
return errorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanUp must be called when done using this wrapper to close the client.
|
||||||
|
// Note that the cleanup will be delayed if auth failure retries were
|
||||||
|
// requested and there is a pending retry.
|
||||||
|
public void cleanUp() {
|
||||||
|
if (retriesPending == 0 && !cleanupCalled) {
|
||||||
|
androidGdataClient.close();
|
||||||
|
}
|
||||||
|
cleanupCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRetryOnAuthFailure(boolean retry) {
|
||||||
|
retryOnAuthFailure = retry;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a "my maps" map.
|
||||||
|
*/
|
||||||
|
class MyMapsMapMetadata {
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
private String description;
|
||||||
|
private String gdataEditUri;
|
||||||
|
private boolean searchable;
|
||||||
|
|
||||||
|
public MyMapsMapMetadata() {
|
||||||
|
title = "";
|
||||||
|
description = "";
|
||||||
|
gdataEditUri = "";
|
||||||
|
searchable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = new String(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = new String(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSearchable() {
|
||||||
|
return searchable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchable(boolean searchable) {
|
||||||
|
this.searchable = searchable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGDataEditUri() {
|
||||||
|
return gdataEditUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGDataEditUri(String editUri) {
|
||||||
|
this.gdataEditUri = new String(editUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
import com.google.wireless.gdata.data.Feed;
|
||||||
|
import com.google.wireless.gdata.data.XmlUtils;
|
||||||
|
import com.google.wireless.gdata.parser.ParseException;
|
||||||
|
import com.google.wireless.gdata.parser.xml.XmlGDataParser;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser for XML gdata maps data.
|
||||||
|
*/
|
||||||
|
class XmlMapsGDataParser extends XmlGDataParser {
|
||||||
|
|
||||||
|
public XmlMapsGDataParser(InputStream is, XmlPullParser xpp)
|
||||||
|
throws ParseException {
|
||||||
|
super(is, xpp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Feed createFeed() {
|
||||||
|
return new Feed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Entry createEntry() {
|
||||||
|
return new MapFeatureEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleExtraElementInFeed(Feed feed) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleExtraLinkInEntry(
|
||||||
|
String rel, String type, String href, Entry entry)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
if (!(entry instanceof MapFeatureEntry)) {
|
||||||
|
throw new IllegalArgumentException("Expected MapFeatureEntry!");
|
||||||
|
}
|
||||||
|
if (rel.endsWith("#view")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.handleExtraLinkInEntry(rel, type, href, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the current entry in the XML document. Assumes that the parser is
|
||||||
|
* currently pointing just after an <entry>.
|
||||||
|
*
|
||||||
|
* @param plainEntry The entry that will be filled.
|
||||||
|
* @throws XmlPullParserException Thrown if the XML cannot be parsed.
|
||||||
|
* @throws IOException Thrown if the underlying inputstream cannot be read.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void handleEntry(Entry plainEntry)
|
||||||
|
throws XmlPullParserException, IOException, ParseException {
|
||||||
|
XmlPullParser parser = getParser();
|
||||||
|
if (!(plainEntry instanceof MapFeatureEntry)) {
|
||||||
|
throw new IllegalArgumentException("Expected MapFeatureEntry!");
|
||||||
|
}
|
||||||
|
MapFeatureEntry entry = (MapFeatureEntry) plainEntry;
|
||||||
|
|
||||||
|
int eventType = parser.getEventType();
|
||||||
|
entry.setPrivacy("public");
|
||||||
|
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||||
|
switch (eventType) {
|
||||||
|
case XmlPullParser.START_TAG:
|
||||||
|
String name = parser.getName();
|
||||||
|
if ("entry".equals(name)) {
|
||||||
|
// stop parsing here.
|
||||||
|
return;
|
||||||
|
} else if ("id".equals(name)) {
|
||||||
|
entry.setId(XmlUtils.extractChildText(parser));
|
||||||
|
} else if ("title".equals(name)) {
|
||||||
|
entry.setTitle(XmlUtils.extractChildText(parser));
|
||||||
|
} else if ("link".equals(name)) {
|
||||||
|
String rel = parser.getAttributeValue(null /* ns */, "rel");
|
||||||
|
String type = parser.getAttributeValue(null /* ns */, "type");
|
||||||
|
String href = parser.getAttributeValue(null /* ns */, "href");
|
||||||
|
if ("edit".equals(rel)) {
|
||||||
|
entry.setEditUri(href);
|
||||||
|
} else if ("alternate".equals(rel) && "text/html".equals(type)) {
|
||||||
|
entry.setHtmlUri(href);
|
||||||
|
} else {
|
||||||
|
handleExtraLinkInEntry(rel, type, href, entry);
|
||||||
|
}
|
||||||
|
} else if ("summary".equals(name)) {
|
||||||
|
entry.setSummary(XmlUtils.extractChildText(parser));
|
||||||
|
} else if ("content".equals(name)) {
|
||||||
|
StringBuilder contentBuilder = new StringBuilder();
|
||||||
|
int parentDepth = parser.getDepth();
|
||||||
|
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
|
||||||
|
int etype = parser.next();
|
||||||
|
switch (etype) {
|
||||||
|
case XmlPullParser.START_TAG:
|
||||||
|
contentBuilder.append('<');
|
||||||
|
contentBuilder.append(parser.getName());
|
||||||
|
contentBuilder.append('>');
|
||||||
|
break;
|
||||||
|
case XmlPullParser.TEXT:
|
||||||
|
contentBuilder.append("<![CDATA[");
|
||||||
|
contentBuilder.append(parser.getText());
|
||||||
|
contentBuilder.append("]]>");
|
||||||
|
break;
|
||||||
|
case XmlPullParser.END_TAG:
|
||||||
|
if (parser.getDepth() > parentDepth) {
|
||||||
|
contentBuilder.append("</");
|
||||||
|
contentBuilder.append(parser.getName());
|
||||||
|
contentBuilder.append('>');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (etype == XmlPullParser.END_TAG
|
||||||
|
&& parser.getDepth() == parentDepth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry.setContent(contentBuilder.toString());
|
||||||
|
} else if ("category".equals(name)) {
|
||||||
|
String category = parser.getAttributeValue(null /* ns */, "term");
|
||||||
|
if (category != null && category.length() > 0) {
|
||||||
|
entry.setCategory(category);
|
||||||
|
}
|
||||||
|
String categoryScheme =
|
||||||
|
parser.getAttributeValue(null /* ns */, "scheme");
|
||||||
|
if (categoryScheme != null && category.length() > 0) {
|
||||||
|
entry.setCategoryScheme(categoryScheme);
|
||||||
|
}
|
||||||
|
} else if ("published".equals(name)) {
|
||||||
|
entry.setPublicationDate(XmlUtils.extractChildText(parser));
|
||||||
|
} else if ("updated".equals(name)) {
|
||||||
|
entry.setUpdateDate(XmlUtils.extractChildText(parser));
|
||||||
|
} else if ("deleted".equals(name)) {
|
||||||
|
entry.setDeleted(true);
|
||||||
|
} else if ("draft".equals(name)) {
|
||||||
|
String draft = XmlUtils.extractChildText(parser);
|
||||||
|
entry.setPrivacy("yes".equals(draft) ? "unlisted" : "public");
|
||||||
|
} else if ("customProperty".equals(name)) {
|
||||||
|
String attrName = parser.getAttributeValue(null, "name");
|
||||||
|
String attrValue = XmlUtils.extractChildText(parser);
|
||||||
|
entry.setAttribute(attrName, attrValue);
|
||||||
|
} else if ("deleted".equals(name)) {
|
||||||
|
entry.setDeleted(true);
|
||||||
|
} else {
|
||||||
|
handleExtraElementInEntry(entry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.client.GDataParserFactory;
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
import com.google.wireless.gdata.parser.GDataParser;
|
||||||
|
import com.google.wireless.gdata.parser.ParseException;
|
||||||
|
import com.google.wireless.gdata.parser.xml.XmlGDataParser;
|
||||||
|
import com.google.wireless.gdata.parser.xml.XmlParserFactory;
|
||||||
|
import com.google.wireless.gdata.serializer.GDataSerializer;
|
||||||
|
import com.google.wireless.gdata.serializer.xml.XmlEntryGDataSerializer;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory of Xml parsers for gdata maps data.
|
||||||
|
*/
|
||||||
|
class XmlMapsGDataParserFactory implements GDataParserFactory {
|
||||||
|
private XmlParserFactory xmlFactory;
|
||||||
|
|
||||||
|
public XmlMapsGDataParserFactory(XmlParserFactory xmlFactory) {
|
||||||
|
this.xmlFactory = xmlFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GDataParser createParser(InputStream is) throws ParseException {
|
||||||
|
is = maybeLogCommunication(is);
|
||||||
|
try {
|
||||||
|
return new XmlGDataParser(is, xmlFactory.createParser());
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
@Override
|
||||||
|
public GDataParser createParser(Class cls, InputStream is)
|
||||||
|
throws ParseException {
|
||||||
|
is = maybeLogCommunication(is);
|
||||||
|
try {
|
||||||
|
return createParserForClass(cls, is);
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream maybeLogCommunication(InputStream is)
|
||||||
|
throws ParseException {
|
||||||
|
if (MapsClient.LOG_COMMUNICATION) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
byte[] buffer = new byte[2048];
|
||||||
|
try {
|
||||||
|
for (int n = is.read(buffer); n >= 0; n = is.read(buffer)) {
|
||||||
|
String part = new String(buffer, 0, n);
|
||||||
|
builder.append(part);
|
||||||
|
Log.d("Response part", part);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ParseException("Could not read stream", e);
|
||||||
|
}
|
||||||
|
String whole = builder.toString();
|
||||||
|
Log.d("Response", whole);
|
||||||
|
is = new ByteArrayInputStream(whole.getBytes());
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GDataParser createParserForClass(
|
||||||
|
Class<? extends Entry> cls, InputStream is)
|
||||||
|
throws ParseException, XmlPullParserException {
|
||||||
|
if (cls == MapFeatureEntry.class) {
|
||||||
|
return new XmlMapsGDataParser(is, xmlFactory.createParser());
|
||||||
|
} else {
|
||||||
|
return new XmlGDataParser(is, xmlFactory.createParser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GDataSerializer createSerializer(Entry en) {
|
||||||
|
if (en.getClass() == MapFeatureEntry.class) {
|
||||||
|
return new XmlMapsGDataSerializer(xmlFactory, en);
|
||||||
|
} else {
|
||||||
|
return new XmlEntryGDataSerializer(xmlFactory, en);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
package com.google.android.apps.mytracks.io.mymaps;
|
||||||
|
|
||||||
|
import com.google.wireless.gdata.data.Entry;
|
||||||
|
import com.google.wireless.gdata.data.StringUtils;
|
||||||
|
import com.google.wireless.gdata.parser.ParseException;
|
||||||
|
import com.google.wireless.gdata.parser.xml.XmlGDataParser;
|
||||||
|
import com.google.wireless.gdata.parser.xml.XmlParserFactory;
|
||||||
|
import com.google.wireless.gdata.serializer.xml.XmlEntryGDataSerializer;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializer of maps data for GData.
|
||||||
|
*/
|
||||||
|
class XmlMapsGDataSerializer extends XmlEntryGDataSerializer {
|
||||||
|
|
||||||
|
private static final String APP_NAMESPACE = "http://www.w3.org/2007/app";
|
||||||
|
|
||||||
|
private MapFeatureEntry entry;
|
||||||
|
private XmlParserFactory factory;
|
||||||
|
private OutputStream stream;
|
||||||
|
|
||||||
|
public XmlMapsGDataSerializer(XmlParserFactory factory, Entry entry) {
|
||||||
|
super(factory, entry);
|
||||||
|
|
||||||
|
this.factory = factory;
|
||||||
|
this.entry = (MapFeatureEntry) entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(OutputStream out, int format)
|
||||||
|
throws IOException, ParseException {
|
||||||
|
XmlSerializer serializer = null;
|
||||||
|
try {
|
||||||
|
serializer = factory.createSerializer();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
throw new ParseException("Unable to create XmlSerializer.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream printStream;
|
||||||
|
|
||||||
|
if (MapsClient.LOG_COMMUNICATION) {
|
||||||
|
printStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
serializer.setOutput(printStream, "UTF-8");
|
||||||
|
} else {
|
||||||
|
serializer.setOutput(out, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer.startDocument("UTF-8", Boolean.FALSE);
|
||||||
|
|
||||||
|
declareEntryNamespaces(serializer);
|
||||||
|
serializer.startTag(XmlGDataParser.NAMESPACE_ATOM_URI, "entry");
|
||||||
|
|
||||||
|
stream = MapsClient.LOG_COMMUNICATION ? printStream : out;
|
||||||
|
serializeEntryContents(serializer, format);
|
||||||
|
|
||||||
|
serializer.endTag(XmlGDataParser.NAMESPACE_ATOM_URI, "entry");
|
||||||
|
serializer.endDocument();
|
||||||
|
serializer.flush();
|
||||||
|
|
||||||
|
if (MapsClient.LOG_COMMUNICATION) {
|
||||||
|
Log.d("Request", printStream.toString());
|
||||||
|
out.write(printStream.toByteArray());
|
||||||
|
stream = out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void declareEntryNamespaces(XmlSerializer serializer)
|
||||||
|
throws IOException {
|
||||||
|
serializer.setPrefix(
|
||||||
|
"" /* default ns */, XmlGDataParser.NAMESPACE_ATOM_URI);
|
||||||
|
serializer.setPrefix(
|
||||||
|
XmlGDataParser.NAMESPACE_GD, XmlGDataParser.NAMESPACE_GD_URI);
|
||||||
|
declareExtraEntryNamespaces(serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final void serializeEntryContents(XmlSerializer serializer,
|
||||||
|
int format) throws IOException {
|
||||||
|
if (format != FORMAT_CREATE) {
|
||||||
|
serializeId(serializer, entry.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeTitle(serializer, entry.getTitle());
|
||||||
|
|
||||||
|
if (format != FORMAT_CREATE) {
|
||||||
|
serializeLink(serializer,
|
||||||
|
"edit" /* rel */, entry.getEditUri(), null /* type */);
|
||||||
|
serializeLink(serializer,
|
||||||
|
"alternate" /* rel */, entry.getHtmlUri(), "text/html" /* type */);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeSummary(serializer, entry.getSummary());
|
||||||
|
serializeContent(serializer, entry.getContent());
|
||||||
|
serializeAuthor(serializer, entry.getAuthor(), entry.getEmail());
|
||||||
|
serializeCategory(serializer,
|
||||||
|
entry.getCategory(), entry.getCategoryScheme());
|
||||||
|
|
||||||
|
if (format == FORMAT_FULL) {
|
||||||
|
serializePublicationDate(serializer, entry.getPublicationDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format != FORMAT_CREATE) {
|
||||||
|
serializeUpdateDate(serializer, entry.getUpdateDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeExtraEntryContents(serializer, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeId(XmlSerializer serializer, String id)
|
||||||
|
throws IOException {
|
||||||
|
if (StringUtils.isEmpty(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "id");
|
||||||
|
serializer.text(id);
|
||||||
|
serializer.endTag(null /* ns */, "id");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeTitle(XmlSerializer serializer, String title)
|
||||||
|
throws IOException {
|
||||||
|
if (StringUtils.isEmpty(title)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "title");
|
||||||
|
serializer.text(title);
|
||||||
|
serializer.endTag(null /* ns */, "title");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serializeLink(XmlSerializer serializer, String rel,
|
||||||
|
String href, String type) throws IOException {
|
||||||
|
if (StringUtils.isEmpty(href)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "link");
|
||||||
|
serializer.attribute(null /* ns */, "rel", rel);
|
||||||
|
serializer.attribute(null /* ns */, "href", href);
|
||||||
|
if (!StringUtils.isEmpty(type)) {
|
||||||
|
serializer.attribute(null /* ns */, "type", type);
|
||||||
|
}
|
||||||
|
serializer.endTag(null /* ns */, "link");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeSummary(XmlSerializer serializer, String summary)
|
||||||
|
throws IOException {
|
||||||
|
if (StringUtils.isEmpty(summary)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "summary");
|
||||||
|
serializer.text(summary);
|
||||||
|
serializer.endTag(null /* ns */, "summary");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void serializeContent(XmlSerializer serializer, String content)
|
||||||
|
throws IOException {
|
||||||
|
if (content == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "content");
|
||||||
|
if (content.contains("</Placemark>")) {
|
||||||
|
serializer.attribute(
|
||||||
|
null /* ns */, "type", "application/vnd.google-earth.kml+xml");
|
||||||
|
serializer.flush();
|
||||||
|
stream.write(content.getBytes());
|
||||||
|
} else {
|
||||||
|
serializer.text(content);
|
||||||
|
}
|
||||||
|
serializer.endTag(null /* ns */, "content");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeAuthor(XmlSerializer serializer, String author,
|
||||||
|
String email) throws IOException {
|
||||||
|
if (StringUtils.isEmpty(author) || StringUtils.isEmpty(email)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "author");
|
||||||
|
serializer.startTag(null /* ns */, "name");
|
||||||
|
serializer.text(author);
|
||||||
|
serializer.endTag(null /* ns */, "name");
|
||||||
|
serializer.startTag(null /* ns */, "email");
|
||||||
|
serializer.text(email);
|
||||||
|
serializer.endTag(null /* ns */, "email");
|
||||||
|
serializer.endTag(null /* ns */, "author");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeCategory(XmlSerializer serializer,
|
||||||
|
String category, String categoryScheme) throws IOException {
|
||||||
|
if (StringUtils.isEmpty(category) && StringUtils.isEmpty(categoryScheme)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "category");
|
||||||
|
if (!StringUtils.isEmpty(category)) {
|
||||||
|
serializer.attribute(null /* ns */, "term", category);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(categoryScheme)) {
|
||||||
|
serializer.attribute(null /* ns */, "scheme", categoryScheme);
|
||||||
|
}
|
||||||
|
serializer.endTag(null /* ns */, "category");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializePublicationDate(XmlSerializer serializer,
|
||||||
|
String publicationDate) throws IOException {
|
||||||
|
if (StringUtils.isEmpty(publicationDate)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "published");
|
||||||
|
serializer.text(publicationDate);
|
||||||
|
serializer.endTag(null /* ns */, "published");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void serializeUpdateDate(XmlSerializer serializer,
|
||||||
|
String updateDate) throws IOException {
|
||||||
|
if (StringUtils.isEmpty(updateDate)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serializer.startTag(null /* ns */, "updated");
|
||||||
|
serializer.text(updateDate);
|
||||||
|
serializer.endTag(null /* ns */, "updated");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void serializeExtraEntryContents(XmlSerializer serializer,
|
||||||
|
int format) throws IOException {
|
||||||
|
Map<String, String> attrs = entry.getAllAttributes();
|
||||||
|
for (Map.Entry<String, String> attr : attrs.entrySet()) {
|
||||||
|
serializer.startTag("http://schemas.google.com/g/2005", "customProperty");
|
||||||
|
serializer.attribute(null, "name", attr.getKey());
|
||||||
|
serializer.text(attr.getValue());
|
||||||
|
serializer.endTag("http://schemas.google.com/g/2005", "customProperty");
|
||||||
|
}
|
||||||
|
String privacy = entry.getPrivacy();
|
||||||
|
if (!StringUtils.isEmpty(privacy)) {
|
||||||
|
serializer.setPrefix("app", APP_NAMESPACE);
|
||||||
|
if ("public".equals(privacy)) {
|
||||||
|
serializer.startTag(APP_NAMESPACE, "control");
|
||||||
|
serializer.startTag(APP_NAMESPACE, "draft");
|
||||||
|
serializer.text("no");
|
||||||
|
serializer.endTag(APP_NAMESPACE, "draft");
|
||||||
|
serializer.endTag(APP_NAMESPACE, "control");
|
||||||
|
}
|
||||||
|
if ("unlisted".equals(privacy)) {
|
||||||
|
serializer.startTag(APP_NAMESPACE, "control");
|
||||||
|
serializer.startTag(APP_NAMESPACE, "draft");
|
||||||
|
serializer.text("yes");
|
||||||
|
serializer.endTag(APP_NAMESPACE, "draft");
|
||||||
|
serializer.endTag(APP_NAMESPACE, "control");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user