Fix an error when run testSplitSetting in French. Update the Robotium to latest version.

This commit is contained in:
youtaol
2012-07-25 18:14:58 +08:00
commit 0e8f926769
689 changed files with 63953 additions and 0 deletions
@@ -0,0 +1,459 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata;
import com.google.wireless.gdata.client.GDataClient;
import com.google.wireless.gdata.client.HttpException;
import com.google.wireless.gdata.client.QueryParams;
import com.google.wireless.gdata.data.StringUtils;
import com.google.wireless.gdata.parser.ParseException;
import com.google.wireless.gdata.serializer.GDataSerializer;
import android.text.TextUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
/**
* Implementation of a GDataClient using GoogleHttpClient to make HTTP requests.
* Always issues GETs and POSTs, using the X-HTTP-Method-Override header when a
* PUT or DELETE is desired, to avoid issues with firewalls, etc., that do not
* allow methods other than GET or POST.
*/
public class AndroidGDataClient implements GDataClient {
private static final String TAG = "GDataClient";
private static final boolean DEBUG = false;
private static final String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
private static final int MAX_REDIRECTS = 10;
private final HttpClient httpClient;
/**
* Interface for creating HTTP requests. Used by
* {@link AndroidGDataClient#createAndExecuteMethod}, since HttpUriRequest
* does not allow for changing the URI after creation, e.g., when you want to
* follow a redirect.
*/
private interface HttpRequestCreator {
HttpUriRequest createRequest(URI uri);
}
private static class GetRequestCreator implements HttpRequestCreator {
public HttpUriRequest createRequest(URI uri) {
return new HttpGet(uri);
}
}
private static class PostRequestCreator implements HttpRequestCreator {
private final String mMethodOverride;
private final HttpEntity mEntity;
public PostRequestCreator(String methodOverride, HttpEntity entity) {
mMethodOverride = methodOverride;
mEntity = entity;
}
public HttpUriRequest createRequest(URI uri) {
HttpPost post = new HttpPost(uri);
if (mMethodOverride != null) {
post.addHeader(X_HTTP_METHOD_OVERRIDE, mMethodOverride);
}
post.setEntity(mEntity);
return post;
}
}
// MAJOR TODO: make this work across redirects (if we can reset the
// InputStream).
// OR, read the bits into a local buffer (yuck, the media could be large).
private static class MediaPutRequestCreator implements HttpRequestCreator {
private final InputStream mMediaInputStream;
private final String mContentType;
public MediaPutRequestCreator(InputStream mediaInputStream,
String contentType) {
mMediaInputStream = mediaInputStream;
mContentType = contentType;
}
public HttpUriRequest createRequest(URI uri) {
HttpPost post = new HttpPost(uri);
post.addHeader(X_HTTP_METHOD_OVERRIDE, "PUT");
InputStreamEntity entity =
new InputStreamEntity(mMediaInputStream, -1 /* read until EOF */);
entity.setContentType(mContentType);
post.setEntity(entity);
return post;
}
}
/**
* Creates a new AndroidGDataClient.
*/
public AndroidGDataClient() {
httpClient = new DefaultHttpClient();
}
public void close() {
}
/*
* (non-Javadoc)
*
* @see GDataClient#encodeUri(java.lang.String)
*/
public String encodeUri(String uri) {
String encodedUri;
try {
encodedUri = URLEncoder.encode(uri, "UTF-8");
} catch (UnsupportedEncodingException uee) {
// should not happen.
Log.e("JakartaGDataClient", "UTF-8 not supported -- should not happen. "
+ "Using default encoding.", uee);
encodedUri = URLEncoder.encode(uri);
}
return encodedUri;
}
/*
* (non-Javadoc)
*
* @see com.google.wireless.gdata.client.GDataClient#createQueryParams()
*/
public QueryParams createQueryParams() {
return new QueryParamsImpl();
}
// follows redirects
private InputStream createAndExecuteMethod(HttpRequestCreator creator,
String uriString, String authToken) throws HttpException, IOException {
HttpResponse response = null;
int status = 500;
int redirectsLeft = MAX_REDIRECTS;
URI uri;
try {
uri = new URI(uriString);
} catch (URISyntaxException use) {
Log.w(TAG, "Unable to parse " + uriString + " as URI.", use);
throw new IOException("Unable to parse " + uriString + " as URI: "
+ use.getMessage());
}
// we follow redirects ourselves, since we want to follow redirects even on
// POSTs, which
// the HTTP library does not do. following redirects ourselves also allows
// us to log
// the redirects using our own logging.
while (redirectsLeft > 0) {
HttpUriRequest request = creator.createRequest(uri);
request.addHeader("User-Agent", "Android-GData");
request.addHeader("Accept-Encoding", "gzip");
// only add the auth token if not null (to allow for GData feeds that do
// not require
// authentication.)
if (!TextUtils.isEmpty(authToken)) {
request.addHeader("Authorization", "GoogleLogin auth=" + authToken);
}
if (DEBUG) {
for (Header h : request.getAllHeaders()) {
Log.v(TAG, h.getName() + ": " + h.getValue());
}
Log.d(TAG, "Executing " + request.getRequestLine().toString());
}
response = null;
try {
response = httpClient.execute(request);
} catch (IOException ioe) {
Log.w(TAG, "Unable to execute HTTP request." + ioe);
throw ioe;
}
StatusLine statusLine = response.getStatusLine();
if (statusLine == null) {
Log.w(TAG, "StatusLine is null.");
throw new NullPointerException(
"StatusLine is null -- should not happen.");
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, response.getStatusLine().toString());
for (Header h : response.getAllHeaders()) {
Log.d(TAG, h.getName() + ": " + h.getValue());
}
}
status = statusLine.getStatusCode();
HttpEntity entity = response.getEntity();
if ((status >= 200) && (status < 300) && entity != null) {
return getUngzippedContent(entity);
}
// TODO: handle 301, 307?
// TODO: let the http client handle the redirects, if we can be sure we'll
// never get a
// redirect on POST.
if (status == 302) {
// consume the content, so the connection can be closed.
entity.consumeContent();
Header location = response.getFirstHeader("Location");
if (location == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Redirect requested but no Location " + "specified.");
}
break;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Following redirect to " + location.getValue());
}
try {
uri = new URI(location.getValue());
} catch (URISyntaxException use) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to parse " + location.getValue() + " as URI.",
use);
throw new IOException("Unable to parse " + location.getValue()
+ " as URI.");
}
break;
}
--redirectsLeft;
} else {
break;
}
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Received " + status + " status code.");
}
String errorMessage = null;
HttpEntity entity = response.getEntity();
try {
if (entity != null) {
InputStream in = entity.getContent();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
int bytesRead = -1;
while ((bytesRead = in.read(buf)) != -1) {
baos.write(buf, 0, bytesRead);
}
// TODO: use appropriate encoding, picked up from Content-Type.
errorMessage = new String(baos.toByteArray());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, errorMessage);
}
}
} finally {
if (entity != null) {
entity.consumeContent();
}
}
String exceptionMessage = "Received " + status + " status code";
if (errorMessage != null) {
exceptionMessage += (": " + errorMessage);
}
throw new HttpException(exceptionMessage, status, null /* InputStream */);
}
/**
* Gets the input stream from a response entity. If the entity is gzipped
* then this will get a stream over the uncompressed data.
*
* @param entity the entity whose content should be read
* @return the input stream to read from
* @throws IOException
*/
private static InputStream getUngzippedContent(HttpEntity entity)
throws IOException {
InputStream responseStream = entity.getContent();
if (responseStream == null) {
return responseStream;
}
Header header = entity.getContentEncoding();
if (header == null) {
return responseStream;
}
String contentEncoding = header.getValue();
if (contentEncoding == null) {
return responseStream;
}
if (contentEncoding.contains("gzip")){
responseStream = new GZIPInputStream(responseStream);
}
return responseStream;
}
/*
* (non-Javadoc)
*
* @see GDataClient#getFeedAsStream(java.lang.String, java.lang.String)
*/
public InputStream getFeedAsStream(String feedUrl, String authToken)
throws HttpException, IOException {
InputStream in =
createAndExecuteMethod(new GetRequestCreator(), feedUrl, authToken);
if (in != null) {
return in;
}
throw new IOException("Unable to access feed.");
}
public InputStream getMediaEntryAsStream(String mediaEntryUrl,
String authToken) throws HttpException, IOException {
InputStream in =
createAndExecuteMethod(new GetRequestCreator(), mediaEntryUrl,
authToken);
if (in != null) {
return in;
}
throw new IOException("Unable to access media entry.");
}
/*
* (non-Javadoc)
*
* @see GDataClient#createEntry
*/
public InputStream createEntry(String feedUrl, String authToken,
GDataSerializer entry) throws HttpException, IOException {
HttpEntity entity =
createEntityForEntry(entry, GDataSerializer.FORMAT_CREATE);
InputStream in =
createAndExecuteMethod(new PostRequestCreator(null /* override */,
entity), feedUrl, authToken);
if (in != null) {
return in;
}
throw new IOException("Unable to create entry.");
}
/*
* (non-Javadoc)
*
* @see GDataClient#updateEntry
*/
public InputStream updateEntry(String editUri, String authToken,
GDataSerializer entry) throws HttpException, IOException {
HttpEntity entity =
createEntityForEntry(entry, GDataSerializer.FORMAT_UPDATE);
InputStream in =
createAndExecuteMethod(new PostRequestCreator("PUT", entity), editUri,
authToken);
if (in != null) {
return in;
}
throw new IOException("Unable to update entry.");
}
/*
* (non-Javadoc)
*
* @see GDataClient#deleteEntry
*/
public void deleteEntry(String editUri, String authToken)
throws HttpException, IOException {
if (StringUtils.isEmpty(editUri)) {
throw new IllegalArgumentException(
"you must specify an non-empty edit url");
}
InputStream in =
createAndExecuteMethod(
new PostRequestCreator("DELETE", null /* entity */), editUri,
authToken);
if (in == null) {
throw new IOException("Unable to delete entry.");
}
try {
in.close();
} catch (IOException ioe) {
// ignore
}
}
public InputStream updateMediaEntry(String editUri, String authToken,
InputStream mediaEntryInputStream, String contentType)
throws HttpException, IOException {
InputStream in =
createAndExecuteMethod(new MediaPutRequestCreator(
mediaEntryInputStream, contentType), editUri, authToken);
if (in != null) {
return in;
}
throw new IOException("Unable to write media entry.");
}
private HttpEntity createEntityForEntry(GDataSerializer entry, int format)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
entry.serialize(baos, format);
} catch (IOException ioe) {
Log.e(TAG, "Unable to serialize entry.", ioe);
throw ioe;
} catch (ParseException pe) {
Log.e(TAG, "Unable to serialize entry.", pe);
throw new IOException("Unable to serialize entry: " + pe.getMessage());
}
byte[] entryBytes = baos.toByteArray();
if (entryBytes != null && Log.isLoggable(TAG, Log.DEBUG)) {
try {
Log.d(TAG, "Serialized entry: " + new String(entryBytes, "UTF-8"));
} catch (UnsupportedEncodingException uee) {
// should not happen
throw new IllegalStateException("UTF-8 should be supported!", uee);
}
}
AbstractHttpEntity entity = new ByteArrayEntity(entryBytes);
entity.setContentType(entry.getContentType());
return entity;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata;
import com.google.android.apps.mytracks.Constants;
import com.google.wireless.gdata.client.GDataClient;
import android.content.Context;
import android.util.Log;
/**
* This factory will fetch the right class for the platform.
*
* @author Sandor Dornbush
*/
public class GDataClientFactory {
private GDataClientFactory() { }
/**
* Creates a new GData client.
* This factory will fetch the right class for the platform.
* @return A GDataClient appropriate for this platform
*/
public static GDataClient getGDataClient(Context context) {
// TODO This should be moved into ApiAdapter
try {
// Try to use the official unbundled gdata client implementation.
// This should work on Froyo and beyond.
return new com.google.android.common.gdata.AndroidGDataClient(context);
} catch (LinkageError e) {
// On all other platforms use the client implementation packaged in the
// apk.
Log.i(Constants.TAG, "Using mytracks AndroidGDataClient.", e);
return new com.google.android.apps.mytracks.io.gdata.AndroidGDataClient();
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata;
import com.google.wireless.gdata.client.QueryParams;
import android.text.TextUtils;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Simple implementation of the QueryParams interface.
*/
// TODO: deal with categories
public class QueryParamsImpl extends QueryParams {
private final Map<String, String> mParams = new HashMap<String, String>();
@Override
public void clear() {
setEntryId(null);
mParams.clear();
}
@Override
public String generateQueryUrl(String feedUrl) {
if (TextUtils.isEmpty(getEntryId()) && mParams.isEmpty()) {
// nothing to do
return feedUrl;
}
// handle entry IDs
if (!TextUtils.isEmpty(getEntryId())) {
if (!mParams.isEmpty()) {
throw new IllegalStateException("Cannot set both an entry ID "
+ "and other query paramters.");
}
return feedUrl + '/' + getEntryId();
}
// otherwise, append the querystring params.
StringBuilder sb = new StringBuilder();
sb.append(feedUrl);
Set<String> params = mParams.keySet();
boolean first = true;
if (feedUrl.contains("?")) {
first = false;
} else {
sb.append('?');
}
for (String param : params) {
if (first) {
first = false;
} else {
sb.append('&');
}
sb.append(param);
sb.append('=');
String value = mParams.get(param);
String encodedValue = null;
try {
encodedValue = URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException uee) {
// should not happen.
Log.w("QueryParamsImpl", "UTF-8 not supported -- should not happen. "
+ "Using default encoding.", uee);
encodedValue = URLEncoder.encode(value);
}
sb.append(encodedValue);
}
return sb.toString();
}
@Override
public String getParamValue(String param) {
if (!(mParams.containsKey(param))) {
return null;
}
return mParams.get(param);
}
@Override
public void setParamValue(String param, String value) {
mParams.put(param, value);
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata.docs;
import com.google.wireless.gdata.client.GDataClient;
import com.google.wireless.gdata.client.GDataParserFactory;
import com.google.wireless.gdata.client.GDataServiceClient;
/**
* GDataServiceClient for accessing Google Documents. This is not a full
* implementation.
*/
public class DocumentsClient extends GDataServiceClient {
/** The name of the service, dictated to be 'wise' by the protocol. */
public static final String SERVICE = "writely";
/**
* Creates a new DocumentsClient.
*
* @param client The GDataClient that should be used to authenticate requests,
* retrieve feeds, etc
* @param parserFactory The GDataParserFactory that should be used to obtain
* GDataParsers used by this client
*/
public DocumentsClient(GDataClient client, GDataParserFactory parserFactory) {
super(client, parserFactory);
}
@Override
public String getServiceName() {
return SERVICE;
}
}
@@ -0,0 +1,269 @@
/*
* Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata.docs;
import com.google.wireless.gdata.client.GDataClient;
import com.google.wireless.gdata.client.GDataParserFactory;
import com.google.wireless.gdata.client.GDataServiceClient;
import com.google.wireless.gdata.client.HttpException;
import com.google.wireless.gdata.data.Entry;
import com.google.wireless.gdata.data.StringUtils;
import com.google.wireless.gdata.parser.GDataParser;
import com.google.wireless.gdata.parser.ParseException;
import com.google.wireless.gdata.serializer.GDataSerializer;
import com.google.wireless.gdata2.client.AuthenticationException;
import java.io.IOException;
import java.io.InputStream;
/**
* GDataServiceClient for accessing Google Spreadsheets. This client can access
* and parse all of the Spreadsheets feed types: Spreadsheets feed, Worksheets
* feed, List feed, and Cells feed. Read operations are supported on all feed
* types, but only the List and Cells feeds support write operations. (This is a
* limitation of the protocol, not this API. Such write access may be added to
* the protocol in the future, requiring changes to this implementation.)
*
* Only 'private' visibility and 'full' projections are currently supported.
*/
public class SpreadsheetsClient extends GDataServiceClient {
/** The name of the service, dictated to be 'wise' by the protocol. */
public static final String SERVICE = "wise";
/** Standard base feed url for spreadsheets. */
public static final String SPREADSHEETS_BASE_FEED_URL =
"http://spreadsheets.google.com/feeds/spreadsheets/private/full";
/**
* Represents an entry in a GData Spreadsheets meta-feed.
*/
public static class SpreadsheetEntry extends Entry { }
/**
* Represents an entry in a GData Worksheets meta-feed.
*/
public static class WorksheetEntry extends Entry { }
/**
* Creates a new SpreadsheetsClient. Uses the standard base URL for
* spreadsheets feeds.
*
* @param client The GDataClient that should be used to authenticate requests,
* retrieve feeds, etc
*/
public SpreadsheetsClient(GDataClient client,
GDataParserFactory spreadsheetFactory) {
super(client, spreadsheetFactory);
}
@Override
public String getServiceName() {
return SERVICE;
}
/**
* Returns a parser for the specified feed type.
*
* @param feedEntryClass the Class of entry type that will be parsed, which
* lets this method figure out which parser to create
* @param feedUri the URI of the feed to be fetched and parsed
* @param authToken the current authToken to use for the request
* @return a parser for the indicated feed
* @throws AuthenticationException if the authToken is not valid
* @throws ParseException if the response from the server could not be parsed
*/
private GDataParser getParserForTypedFeed(
Class<? extends Entry> feedEntryClass, String feedUri, String authToken)
throws AuthenticationException, ParseException, IOException {
GDataClient gDataClient = getGDataClient();
GDataParserFactory gDataParserFactory = getGDataParserFactory();
try {
InputStream is = gDataClient.getFeedAsStream(feedUri, authToken);
return gDataParserFactory.createParser(feedEntryClass, is);
} catch (HttpException e) {
convertHttpExceptionForReads("Could not fetch parser feed.", e);
return null; // never reached
}
}
/**
* Converts an HTTP exception that happened while reading into the equivalent
* local exception.
*/
public void convertHttpExceptionForReads(String message, HttpException cause)
throws AuthenticationException, IOException {
switch (cause.getStatusCode()) {
case HttpException.SC_FORBIDDEN:
case HttpException.SC_UNAUTHORIZED:
throw new AuthenticationException(message, cause);
case HttpException.SC_GONE:
default:
throw new IOException(message + ": " + cause.getMessage());
}
}
@Override
public Entry createEntry(String feedUri, String authToken, Entry entry)
throws ParseException, IOException {
GDataParserFactory factory = getGDataParserFactory();
GDataSerializer serializer = factory.createSerializer(entry);
InputStream is;
try {
is = getGDataClient().createEntry(feedUri, authToken, serializer);
} catch (HttpException e) {
convertHttpExceptionForWrites(entry.getClass(),
"Could not update entry.", e);
return null; // never reached.
}
GDataParser parser = factory.createParser(entry.getClass(), is);
try {
return parser.parseStandaloneEntry();
} finally {
parser.close();
}
}
/**
* Fetches a GDataParser for the indicated feed. The parser can be used to
* access the contents of URI. WARNING: because we cannot reliably infer the
* feed type from the URI alone, this method assumes the default feed type!
* This is probably NOT what you want. Please use the getParserFor[Type]Feed
* methods.
*
* @param feedEntryClass
* @param feedUri the URI of the feed to be fetched and parsed
* @param authToken the current authToken to use for the request
* @return a parser for the indicated feed
* @throws ParseException if the response from the server could not be parsed
*/
@SuppressWarnings("rawtypes")
@Override
public GDataParser getParserForFeed(
Class feedEntryClass, String feedUri, String authToken)
throws ParseException, IOException {
try {
return getParserForTypedFeed(SpreadsheetEntry.class, feedUri, authToken);
} catch (AuthenticationException e) {
throw new IOException("Authentication Failure: " + e.getMessage());
}
}
/**
* Returns a parser for a Worksheets meta-feed.
*
* @param feedUri the URI of the feed to be fetched and parsed
* @param authToken the current authToken to use for the request
* @return a parser for the indicated feed
* @throws AuthenticationException if the authToken is not valid
* @throws ParseException if the response from the server could not be parsed
*/
public GDataParser getParserForWorksheetsFeed(
String feedUri, String authToken)
throws AuthenticationException, ParseException, IOException {
return getParserForTypedFeed(WorksheetEntry.class, feedUri, authToken);
}
/**
* Updates an entry. The URI to be updated is taken from <code>entry</code>.
* Note that only entries in List and Cells feeds can be updated, so
* <code>entry</code> must be of the corresponding type; other types will
* result in an exception.
*
* @param entry the entry to be updated; must include its URI
* @param authToken the current authToken to be used for the operation
* @return An Entry containing the re-parsed version of the entry returned by
* the server in response to the update
* @throws ParseException if the server returned an error, if the server's
* response was unparseable (unlikely), or if <code>entry</code> is of
* a read-only type
* @throws IOException on network error
*/
@Override
public Entry updateEntry(Entry entry, String authToken)
throws ParseException, IOException {
GDataParserFactory factory = getGDataParserFactory();
GDataSerializer serializer = factory.createSerializer(entry);
String editUri = entry.getEditUri();
if (StringUtils.isEmpty(editUri)) {
throw new ParseException("No edit URI -- cannot update.");
}
InputStream is;
try {
is = getGDataClient().updateEntry(editUri, authToken, serializer);
} catch (HttpException e) {
convertHttpExceptionForWrites(entry.getClass(),
"Could not update entry.", e);
return null; // never reached
}
GDataParser parser = factory.createParser(entry.getClass(), is);
try {
return parser.parseStandaloneEntry();
} finally {
parser.close();
}
}
/**
* Converts an HTTP exception which happened while writing to the equivalent
* local exception.
*/
@SuppressWarnings("rawtypes")
private void convertHttpExceptionForWrites(
Class entryClass, String message, HttpException cause)
throws ParseException, IOException {
switch (cause.getStatusCode()) {
case HttpException.SC_CONFLICT:
if (entryClass != null) {
InputStream is = cause.getResponseStream();
if (is != null) {
parseEntry(entryClass, cause.getResponseStream());
}
}
throw new IOException(message);
case HttpException.SC_BAD_REQUEST:
throw new ParseException(message + ": " + cause);
case HttpException.SC_FORBIDDEN:
case HttpException.SC_UNAUTHORIZED:
throw new IOException(message);
default:
throw new IOException(message + ": " + cause.getMessage());
}
}
/**
* Parses one entry from the input stream.
*/
@SuppressWarnings("rawtypes")
private Entry parseEntry(Class entryClass, InputStream is)
throws ParseException, IOException {
GDataParser parser = null;
try {
parser = getGDataParserFactory().createParser(entryClass, is);
return parser.parseStandaloneEntry();
} finally {
if (parser != null) {
parser.close();
}
}
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.io.gdata.docs;
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 java.io.InputStream;
import org.xmlpull.v1.XmlPullParserException;
/**
* Factory of Xml parsers for gdata maps data.
*/
public class XmlDocsGDataParserFactory implements GDataParserFactory {
private XmlParserFactory xmlFactory;
public XmlDocsGDataParserFactory(XmlParserFactory xmlFactory) {
this.xmlFactory = xmlFactory;
}
@Override
public GDataParser createParser(InputStream is) throws ParseException {
try {
return new XmlGDataParser(is, xmlFactory.createParser());
} catch (XmlPullParserException e) {
e.printStackTrace();
return null;
}
}
@SuppressWarnings("rawtypes")
@Override
public GDataParser createParser(Class cls, InputStream is)
throws ParseException {
try {
return createParserForClass(is);
} catch (XmlPullParserException e) {
e.printStackTrace();
return null;
}
}
private GDataParser createParserForClass(InputStream is)
throws ParseException, XmlPullParserException {
return new XmlGDataParser(is, xmlFactory.createParser());
}
@Override
public GDataSerializer createSerializer(Entry en) {
return new XmlEntryGDataSerializer(xmlFactory, en);
}
}
@@ -0,0 +1,36 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
import com.google.wireless.gdata.data.Entry;
import java.util.HashMap;
import java.util.Map;
/**
* GData entry for a map feature.
*/
public 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,102 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
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;
import java.util.Locale;
/**
* Client to talk to Google Maps via GData.
*/
public 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 MapsConstants.SERVICE_NAME;
}
public static String buildMapUrl(String mapId) {
return MapsConstants.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(Locale.US, MAPS_VERSION_FEED_PATH_FORMAT, MAPS_BASE_FEED_URL,
versionUserId, versionClient, currentVersion);
}
}
@@ -0,0 +1,16 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
/**
* Constants for Google Maps.
*/
public class MapsConstants {
static final String MAPSHOP_BASE_URL =
"https://maps.google.com/maps/ms";
public static final String SERVICE_NAME = "local";
/**
* Private constructor to prevent instantiation.
*/
private MapsConstants() { }
}
@@ -0,0 +1,233 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
import com.google.android.maps.GeoPoint;
import java.util.Random;
import java.util.Vector;
/**
* MapsFeature contains all of the data associated with a feature in Google
* Maps, where a feature is a marker, line, or shape. Some of the data is stored
* in a {@link MapsFeatureMetadata} object so that it can be more efficiently
* transmitted to other activities.
*/
public class MapsFeature {
/** 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 MapsFeatureMetadata featureInfo = new MapsFeatureMetadata();
private final Random random = new Random();
/**
* 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 MapsFeature() {
}
/**
* Adds a new point to the end of this feature.
*
* @param point The new point to add
*/
public 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 void generateAndroidId() {
long time = System.currentTimeMillis();
int rand = random.nextInt(10000);
androidId = time + "." + rand;
}
/**
* 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 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 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 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 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 void setIconUrl(String url) {
featureInfo.setIconUrl(url);
}
}
@@ -0,0 +1,88 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
/**
* Metadata about a maps feature.
*/
class MapsFeatureMetadata {
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 MapsFeatureMetadata() {
title = "";
description = "";
type = MapsFeature.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 = url;
}
}
@@ -0,0 +1,178 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
import com.google.wireless.gdata.data.Entry;
import com.google.wireless.gdata.data.StringUtils;
import android.graphics.Color;
import android.util.Log;
import java.io.IOException;
import java.io.StringWriter;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
/**
* Converter from GData objects to Maps objects.
*/
public class MapsGDataConverter {
private final XmlSerializer xmlSerializer;
public MapsGDataConverter() throws XmlPullParserException {
xmlSerializer = XmlPullParserFactory.newInstance().newSerializer();
}
public static MapsMapMetadata getMapMetadataForEntry(
MapFeatureEntry entry) {
MapsMapMetadata metadata = new MapsMapMetadata();
if ("public".equals(entry.getPrivacy())) {
metadata.setSearchable(true);
} else {
metadata.setSearchable(false);
}
metadata.setTitle(entry.getTitle());
metadata.setDescription(entry.getSummary());
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(MapsMapMetadata metadata) {
MapFeatureEntry entry = new MapFeatureEntry();
entry.setEditUri(metadata.getGDataEditUri());
entry.setTitle(metadata.getTitle());
entry.setSummary(metadata.getDescription());
entry.setPrivacy(metadata.getSearchable() ? "public" : "unlisted");
entry.setAuthor("android");
entry.setEmail("nobody@google.com");
return entry;
}
public MapFeatureEntry getEntryForFeature(MapsFeature 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() == MapsFeature.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() == MapsFeature.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() == MapsFeature.MARKER) {
xmlSerializer.startTag(null, "Point");
xmlSerializer.startTag(null, "coordinates");
xmlSerializer.text(pointString);
xmlSerializer.endTag(null, "coordinates");
xmlSerializer.endTag(null, "Point");
} else if (feature.getType() == MapsFeature.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", "Edit URI: " + entry.getEditUri());
} catch (IOException e) {
e.printStackTrace();
}
return entry;
}
}
@@ -0,0 +1,52 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
/**
* Metadata about a Google Maps map.
*/
public class MapsMapMetadata {
private String title;
private String description;
private String gdataEditUri;
private boolean searchable;
public MapsMapMetadata() {
title = "";
description = "";
gdataEditUri = "";
searchable = false;
}
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 boolean getSearchable() {
return searchable;
}
public void setSearchable(boolean searchable) {
this.searchable = searchable;
}
public String getGDataEditUri() {
return gdataEditUri;
}
public void setGDataEditUri(String editUri) {
this.gdataEditUri = editUri;
}
}
@@ -0,0 +1,163 @@
// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
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 &lt;entry&gt;.
*
* @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();
}
}
}
@@ -0,0 +1,94 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
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 java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.xmlpull.v1.XmlPullParserException;
/**
* Factory of Xml parsers for gdata maps data.
*/
public 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 instanceof MapFeatureEntry) {
return new XmlMapsGDataSerializer(xmlFactory, (MapFeatureEntry) en);
} else {
return new XmlEntryGDataSerializer(xmlFactory, en);
}
}
}
@@ -0,0 +1,262 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.gdata.maps;
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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
/**
* 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, MapFeatureEntry entry) {
super(factory, entry);
this.factory = factory;
this.entry = 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");
if (MapsClient.LOG_COMMUNICATION) {
stream = printStream;
} else {
stream = 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");
}
}
}
}