mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Upgrade send to fusion table to Fusion Table api v1.
This commit is contained in:
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.io.drive;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
@@ -69,8 +70,8 @@ public class SendDriveAsyncTask extends AbstractSendAsyncTask {
|
||||
@Override
|
||||
protected boolean performTask() {
|
||||
try {
|
||||
GoogleAccountCredential credential = SyncUtils.getGoogleAccountCredential(
|
||||
context, account.name);
|
||||
GoogleAccountCredential credential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
context, account.name, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (credential == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -102,16 +103,18 @@ public class SendDriveAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
addPermission(drive, id);
|
||||
return true;
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
return retryTask();
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
return false;
|
||||
} catch (GoogleAuthException e) {
|
||||
return retryTask();
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
return retryTask();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+153
-256
@@ -11,39 +11,33 @@ import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsUpdater;
|
||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.googleapis.GoogleHeaders;
|
||||
import com.google.api.client.googleapis.MethodOverride;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.InputStreamContent;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.api.client.extensions.android.http.AndroidHttp;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.services.fusiontables.Fusiontables;
|
||||
import com.google.api.services.fusiontables.Fusiontables.Query.Sql;
|
||||
import com.google.api.services.fusiontables.model.Column;
|
||||
import com.google.api.services.fusiontables.model.PointStyle;
|
||||
import com.google.api.services.fusiontables.model.StyleFunction;
|
||||
import com.google.api.services.fusiontables.model.StyleSetting;
|
||||
import com.google.api.services.fusiontables.model.Table;
|
||||
import com.google.api.services.fusiontables.model.Template;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* AsyncTask to send a track to Google Fusion Tables.
|
||||
@@ -52,20 +46,10 @@ import java.util.Vector;
|
||||
*/
|
||||
public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String APP_NAME_PREFIX = "Google-MyTracks-";
|
||||
private static final String SQL_KEY = "sql=";
|
||||
@VisibleForTesting
|
||||
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
@VisibleForTesting
|
||||
public static final String
|
||||
FUSION_TABLES_BASE_URL = "https://www.google.com/fusiontables/api/query";
|
||||
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
||||
@VisibleForTesting
|
||||
public static final String GDATA_VERSION = "2";
|
||||
|
||||
private static final int PROGRESS_CREATE_TABLE = 0;
|
||||
private static final int PROGRESS_UNLIST_TABLE = 5;
|
||||
private static final int PROGRESS_SET_STYLE = 5;
|
||||
private static final int PROGRESS_UPLOAD_DATA_MIN = 10;
|
||||
private static final int PROGRESS_UPLOAD_DATA_MAX = 90;
|
||||
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
||||
@@ -78,17 +62,11 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
private static final String MARKER_TYPE_WAYPOINT = "large_blue";
|
||||
private static final String MARKER_TYPE_STATISTICS = "large_yellow";
|
||||
|
||||
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final long trackId;
|
||||
private final Account account;
|
||||
private final Context context;
|
||||
private final MyTracksProviderUtils myTracksProviderUtils;
|
||||
private final HttpRequestFactory httpRequestFactory;
|
||||
|
||||
// The following variables are for per upload states
|
||||
private String authToken;
|
||||
private String tableId;
|
||||
int currentSegment;
|
||||
|
||||
public SendFusionTablesAsyncTask(
|
||||
@@ -96,119 +74,131 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
super(activity);
|
||||
this.trackId = trackId;
|
||||
this.account = account;
|
||||
|
||||
context = activity.getApplicationContext();
|
||||
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
HttpTransport transport = ApiAdapterFactory.getApiAdapter().getHttpTransport();
|
||||
httpRequestFactory = transport.createRequestFactory(new MethodOverride());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeConnection() {
|
||||
// No action needed for Google Fusion Tables
|
||||
}
|
||||
protected void closeConnection() {}
|
||||
|
||||
@Override
|
||||
protected void saveResult() {
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.d(TAG, "No track for " + trackId);
|
||||
return;
|
||||
}
|
||||
track.setTableId(tableId);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
}
|
||||
protected void saveResult() {}
|
||||
|
||||
@Override
|
||||
protected boolean performTask() {
|
||||
// Reset the per upload states
|
||||
authToken = null;
|
||||
tableId = null;
|
||||
currentSegment = 1;
|
||||
|
||||
try {
|
||||
authToken = AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, SendFusionTablesUtils.SERVICE, false);
|
||||
} catch (OperationCanceledException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
} catch (AuthenticatorException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
// Reset the per upload states
|
||||
currentSegment = 1;
|
||||
|
||||
GoogleAccountCredential credential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
context, account.name, SendToGoogleUtils.FUSION_TABLES_SCOPE);
|
||||
if (credential == null) {
|
||||
return false;
|
||||
}
|
||||
Fusiontables fusiontables = new Fusiontables.Builder(
|
||||
AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential).build();
|
||||
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.d(TAG, "No track for " + trackId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new table
|
||||
publishProgress(PROGRESS_CREATE_TABLE);
|
||||
String tableId = createNewTable(fusiontables, track);
|
||||
if (tableId == null) {
|
||||
return retryTask();
|
||||
}
|
||||
|
||||
publishProgress(PROGRESS_SET_STYLE);
|
||||
setStyle(fusiontables, tableId);
|
||||
setTemplate(fusiontables, tableId);
|
||||
|
||||
// Upload all the track points plus the start and end markers
|
||||
publishProgress(PROGRESS_UPLOAD_DATA_MIN);
|
||||
if (!uploadAllTrackPoints(fusiontables, tableId, track)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload all the waypoints
|
||||
publishProgress(PROGRESS_UPLOAD_WAYPOINTS);
|
||||
if (!uploadWaypoints(fusiontables, tableId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
track.setTableId(tableId);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
|
||||
publishProgress(PROGRESS_COMPLETE);
|
||||
return true;
|
||||
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.FUSION_TABLES_NOTIFICATION_ID);
|
||||
return false;
|
||||
} catch (GoogleAuthException e) {
|
||||
return retryTask();
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.FUSION_TABLES_NOTIFICATION_ID);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
}
|
||||
|
||||
Track track = myTracksProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.d(TAG, "No track for " + trackId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new table
|
||||
publishProgress(PROGRESS_CREATE_TABLE);
|
||||
if (!createNewTable(track)) {
|
||||
// Retry upload in case the auth token is invalid
|
||||
return retryTask();
|
||||
}
|
||||
|
||||
// Unlist table
|
||||
publishProgress(PROGRESS_UNLIST_TABLE);
|
||||
if (!unlistTable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload all the track points plus the start and end markers
|
||||
publishProgress(PROGRESS_UPLOAD_DATA_MIN);
|
||||
if (!uploadAllTrackPoints(track)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload all the waypoints
|
||||
publishProgress(PROGRESS_UPLOAD_WAYPOINTS);
|
||||
if (!uploadWaypoints()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
publishProgress(PROGRESS_COMPLETE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateToken() {
|
||||
AccountManager.get(context).invalidateAuthToken(Constants.ACCOUNT_TYPE, authToken);
|
||||
}
|
||||
protected void invalidateToken() {}
|
||||
|
||||
/**
|
||||
* Creates a new table.
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
* @return the table id if success.
|
||||
*/
|
||||
private boolean createNewTable(Track track) {
|
||||
String query = "CREATE TABLE '" + SendFusionTablesUtils.escapeSqlString(track.getName())
|
||||
+ "' (name:STRING,description:STRING,geometry:LOCATION,marker:STRING)";
|
||||
return sendQuery(query, true);
|
||||
private String createNewTable(Fusiontables fusiontables, Track track) throws IOException {
|
||||
Table table = new Table();
|
||||
table.setName(track.getName());
|
||||
table.setDescription(track.getDescription());
|
||||
table.setIsExportable(true);
|
||||
table.setColumns(Arrays.asList(new Column().setName("name").setType("STRING"),
|
||||
new Column().setName("description").setType("STRING"),
|
||||
new Column().setName("geometry").setType("LOCATION"),
|
||||
new Column().setName("icon").setType("STRING")));
|
||||
return fusiontables.table().insert(table).execute().getTableId();
|
||||
}
|
||||
|
||||
private void setStyle(Fusiontables fusiontables, String tableId) throws IOException {
|
||||
StyleFunction styleFunction = new StyleFunction();
|
||||
styleFunction.setColumnName("icon");
|
||||
PointStyle pointStyle = new PointStyle();
|
||||
pointStyle.setIconStyler(styleFunction);
|
||||
StyleSetting styleSetting = new StyleSetting();
|
||||
styleSetting.setTableId(tableId);
|
||||
styleSetting.setMarkerOptions(pointStyle);
|
||||
|
||||
fusiontables.style().insert(tableId, styleSetting).execute();
|
||||
}
|
||||
|
||||
private void setTemplate(Fusiontables fusiontables, String tableId) throws IOException {
|
||||
Template template = new Template();
|
||||
template.setTableId(tableId);
|
||||
template.setAutomaticColumnNames(Arrays.asList("name", "description"));
|
||||
fusiontables.template().insert(tableId, template).execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlists a table.
|
||||
*
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean unlistTable() {
|
||||
String query = "UPDATE TABLE " + tableId + " SET VISIBILITY = UNLISTED";
|
||||
return sendQuery(query, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads all the points in a track.
|
||||
* Uploads all the points in a track. *
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param tableId the table id
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean uploadAllTrackPoints(Track track) {
|
||||
private boolean uploadAllTrackPoints(Fusiontables fusiontables, String tableId, Track track)
|
||||
throws IOException {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTrackPointCursor(trackId, -1L, -1, false);
|
||||
@@ -221,15 +211,6 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastLocation = null;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
int elevationSamplingFrequency = Math.max(1, (int) (count / 250.0));
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
TripStatisticsUpdater tripStatisticsUpdater = new TripStatisticsUpdater(
|
||||
track.getTripStatistics().getStartTime());
|
||||
int minRecordingDistance = PreferencesUtils.getInt(context,
|
||||
R.string.min_recording_distance_key, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
|
||||
@@ -239,17 +220,9 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
if (i == 0) {
|
||||
// Create a start marker
|
||||
String name = context.getString(R.string.marker_label_start, track.getName());
|
||||
if (!createNewPoint(name, "", location, MARKER_TYPE_START)) {
|
||||
Log.d(TAG, "Unable to create the start marker");
|
||||
return false;
|
||||
}
|
||||
createNewPoint(fusiontables, tableId, name, "", location, MARKER_TYPE_START);
|
||||
}
|
||||
|
||||
tripStatisticsUpdater.addLocation(location, minRecordingDistance);
|
||||
if (i % elevationSamplingFrequency == 0) {
|
||||
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
|
||||
elevations.add(tripStatisticsUpdater.getSmoothedElevation());
|
||||
}
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
lastLocation = location;
|
||||
}
|
||||
@@ -257,7 +230,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
// Upload periodically
|
||||
int readCount = i + 1;
|
||||
if (readCount % MAX_POINTS_PER_UPLOAD == 0) {
|
||||
if (!prepareAndUploadPoints(track, locations, false)) {
|
||||
if (!prepareAndUploadPoints(fusiontables, tableId, track, locations, false)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
@@ -267,25 +240,18 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
// Do a final upload with the remaining locations
|
||||
if (!prepareAndUploadPoints(track, locations, true)) {
|
||||
if (!prepareAndUploadPoints(fusiontables, tableId, track, locations, true)) {
|
||||
Log.d(TAG, "Unable to upload points");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an end marker
|
||||
if (lastLocation != null) {
|
||||
distances.add(tripStatisticsUpdater.getTripStatistics().getTotalDistance());
|
||||
elevations.add(tripStatisticsUpdater.getSmoothedElevation());
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
track.setDescription(
|
||||
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||
if (lastLocation != null) {
|
||||
String name = context.getString(R.string.marker_label_end, track.getName());
|
||||
if (!createNewPoint(name, track.getDescription(), lastLocation, MARKER_TYPE_END)) {
|
||||
Log.d(TAG, "Unable to create the end marker");
|
||||
return false;
|
||||
}
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
String description = descriptionGenerator.generateTrackDescription(track, null, null, true);
|
||||
createNewPoint(fusiontables, tableId, name, description, lastLocation, MARKER_TYPE_END);
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
@@ -297,11 +263,14 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
/**
|
||||
* Prepares and uploads a list of locations from a track.
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param tableId the table id
|
||||
* @param track the track
|
||||
* @param locations the locations from the track
|
||||
* @param lastBatch true if it is the last batch of locations
|
||||
*/
|
||||
private boolean prepareAndUploadPoints(Track track, List<Location> locations, boolean lastBatch) {
|
||||
private boolean prepareAndUploadPoints(Fusiontables fusiontables, String tableId, Track track,
|
||||
List<Location> locations, boolean lastBatch) throws IOException {
|
||||
// Prepare locations
|
||||
ArrayList<Track> splitTracks = SendToGoogleUtils.prepareLocations(track, locations);
|
||||
|
||||
@@ -312,10 +281,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
splitTrack.setName(context.getString(
|
||||
R.string.send_google_track_part_label, splitTrack.getName(), currentSegment));
|
||||
}
|
||||
if (!createNewLineString(splitTrack)) {
|
||||
Log.d(TAG, "Upload points failed");
|
||||
return false;
|
||||
}
|
||||
createNewLineString(fusiontables, tableId, splitTrack);
|
||||
currentSegment++;
|
||||
}
|
||||
return true;
|
||||
@@ -324,24 +290,28 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
/**
|
||||
* Uploads all the waypoints.
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param tableId the table id
|
||||
* @return true if success.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean uploadWaypoints() {
|
||||
private boolean uploadWaypoints(Fusiontables fusiontables, String tableId) throws IOException {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getWaypointCursor(
|
||||
trackId, -1L, Constants.MAX_LOADED_WAYPOINTS_POINTS);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
// This will skip the first waypoint (it carries the stats for the
|
||||
// track).
|
||||
/*
|
||||
* This will skip the first waypoint (it carries the stats for the
|
||||
* track).
|
||||
*/
|
||||
while (cursor.moveToNext()) {
|
||||
Waypoint wpt = myTracksProviderUtils.createWaypoint(cursor);
|
||||
String type = wpt.getType() == WaypointType.STATISTICS ? MARKER_TYPE_STATISTICS
|
||||
: MARKER_TYPE_WAYPOINT;
|
||||
if (!createNewPoint(wpt.getName(), wpt.getDescription(), wpt.getLocation(), type)) {
|
||||
Log.d(TAG, "Upload waypoints failed");
|
||||
return false;
|
||||
}
|
||||
String description = wpt.getDescription().replaceAll("\n", "<br>");
|
||||
createNewPoint(
|
||||
fusiontables, tableId, wpt.getName(), description, wpt.getLocation(), type);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -355,110 +325,37 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
/**
|
||||
* Creates a new row in Google Fusion Tables representing a marker as a point.
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param tableId the table id
|
||||
* @param name the marker name
|
||||
* @param description the marker description
|
||||
* @param location the marker location
|
||||
* @param type the marker type
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean createNewPoint(String name, String description, Location location, String type) {
|
||||
String query = "INSERT INTO " + tableId + " (name,description,geometry,marker) VALUES "
|
||||
+ SendFusionTablesUtils.formatSqlValues(
|
||||
name, description, SendFusionTablesUtils.getKmlPoint(location), type);
|
||||
return sendQuery(query, false);
|
||||
private void createNewPoint(Fusiontables fusiontables, String tableId, String name,
|
||||
String description, Location location, String type) throws IOException {
|
||||
String values = SendFusionTablesUtils.formatSqlValues(
|
||||
name, description, SendFusionTablesUtils.getKmlPoint(location), type);
|
||||
Sql sql = fusiontables.query()
|
||||
.sql("INSERT INTO " + tableId + " (name,description,geometry,icon) VALUES " + values);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new row in Google Fusion Tables representing the track as a line
|
||||
* segment.
|
||||
*
|
||||
* @param fusiontables fusion tables
|
||||
* @param tableId the table id
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean createNewLineString(Track track) {
|
||||
String query = "INSERT INTO " + tableId
|
||||
+ " (name,description,geometry) VALUES " + SendFusionTablesUtils.formatSqlValues(
|
||||
track.getName(), track.getDescription(),
|
||||
SendFusionTablesUtils.getKmlLineString(track.getLocations()));
|
||||
return sendQuery(query, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a query to Google Fusion Tables.
|
||||
*
|
||||
* @param query the Fusion Tables SQL query
|
||||
* @param setTableId true to set the table id
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean sendQuery(String query, boolean setTableId) {
|
||||
Log.d(TAG, "SendQuery: " + query);
|
||||
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GenericUrl url = new GenericUrl(FUSION_TABLES_BASE_URL);
|
||||
String sql;
|
||||
try {
|
||||
sql = SQL_KEY + URLEncoder.encode(query, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e1) {
|
||||
Log.d(TAG, "Unable to encode query", e1);
|
||||
return false;
|
||||
}
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(sql.getBytes());
|
||||
InputStreamContent inputStreamContent = new InputStreamContent(null, inputStream);
|
||||
HttpRequest request;
|
||||
try {
|
||||
request = httpRequestFactory.buildPostRequest(url, inputStreamContent);
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to build request", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
GoogleHeaders headers = new GoogleHeaders();
|
||||
headers.setApplicationName(APP_NAME_PREFIX + SystemUtils.getMyTracksVersion(context));
|
||||
headers.setGDataVersion(GDATA_VERSION);
|
||||
headers.setGoogleLogin(authToken);
|
||||
headers.setContentType(CONTENT_TYPE);
|
||||
request.setHeaders(headers);
|
||||
|
||||
HttpResponse response;
|
||||
try {
|
||||
response = request.execute();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to execute request", e);
|
||||
return false;
|
||||
}
|
||||
boolean isSuccess = response.isSuccessStatusCode();
|
||||
if (isSuccess) {
|
||||
InputStream content = null;
|
||||
try {
|
||||
content = response.getContent();
|
||||
if (setTableId) {
|
||||
tableId = SendFusionTablesUtils.getTableId(content);
|
||||
if (tableId == null) {
|
||||
Log.d(TAG, "tableId is null");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to get response", e);
|
||||
return false;
|
||||
} finally {
|
||||
if (content != null) {
|
||||
try {
|
||||
content.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to close content", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG,
|
||||
"sendQuery failed: " + response.getStatusMessage() + ": " + response.getStatusCode());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
private void createNewLineString(Fusiontables fusiontables, String tableId, Track track)
|
||||
throws IOException {
|
||||
String values = SendFusionTablesUtils.formatSqlValues(track.getName(), track.getDescription(),
|
||||
SendFusionTablesUtils.getKmlLineString(track.getLocations()));
|
||||
Sql sql = fusiontables.query()
|
||||
.sql("INSERT INTO " + tableId + " (name,description,geometry) VALUES " + values);
|
||||
sql.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-45
@@ -22,9 +22,6 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -107,7 +104,8 @@ public class SendFusionTablesUtils {
|
||||
* @param string the string
|
||||
* @return the escaped string.
|
||||
*/
|
||||
public static String escapeSqlString(String string) {
|
||||
@VisibleForTesting
|
||||
static String escapeSqlString(String string) {
|
||||
return string.replaceAll("'", "''");
|
||||
}
|
||||
|
||||
@@ -160,45 +158,4 @@ public class SendFusionTablesUtils {
|
||||
builder.append(location.getAltitude());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table id from an input streawm.
|
||||
*
|
||||
* @param inputStream input stream
|
||||
* @return table id or null if not available.
|
||||
*/
|
||||
public static String getTableId(InputStream inputStream) {
|
||||
if (inputStream == null) {
|
||||
Log.d(TAG, "inputStream is null");
|
||||
return null;
|
||||
}
|
||||
byte[] result = new byte[1024];
|
||||
int read;
|
||||
try {
|
||||
read = inputStream.read(result);
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to read result", e);
|
||||
return null;
|
||||
}
|
||||
if (read == -1) {
|
||||
Log.d(TAG, "no data read");
|
||||
return null;
|
||||
}
|
||||
String s;
|
||||
try {
|
||||
s = new String(result, 0, read, UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.d(TAG, "Unable to parse result", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] lines = s.split("\n");
|
||||
if (lines.length > 1 && lines[0].equals(TABLE_ID)) {
|
||||
// returns the next line
|
||||
return lines[1];
|
||||
} else {
|
||||
Log.d(TAG, "Response is not valid: " + s);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+74
-61
@@ -20,13 +20,11 @@ import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.io.docs.SendDocsActivity;
|
||||
import com.google.android.apps.mytracks.io.drive.SendDriveActivity;
|
||||
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesActivity;
|
||||
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesUtils;
|
||||
import com.google.android.apps.mytracks.io.gdata.docs.DocumentsClient;
|
||||
import com.google.android.apps.mytracks.io.gdata.docs.SpreadsheetsClient;
|
||||
import com.google.android.apps.mytracks.io.gdata.maps.MapsConstants;
|
||||
import com.google.android.apps.mytracks.io.maps.ChooseMapActivity;
|
||||
import com.google.android.apps.mytracks.io.maps.SendMapsActivity;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
@@ -50,12 +48,12 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A chooser to select an account.
|
||||
*
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AccountChooserActivity extends Activity {
|
||||
|
||||
private static final String TAG = AccountChooserActivity.class.getSimpleName();
|
||||
private static final String TAG = AccountChooserActivity.class.getSimpleName();
|
||||
private static final int DIALOG_NO_ACCOUNT_ID = 0;
|
||||
private static final int DIALOG_CHOOSER_ID = 1;
|
||||
|
||||
@@ -72,20 +70,20 @@ public class AccountChooserActivity extends Activity {
|
||||
showDialog(DIALOG_NO_ACCOUNT_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (accounts.length == 1) {
|
||||
sendRequest.setAccount(accounts[0]);
|
||||
PreferencesUtils.setString(this, R.string.google_account_key, accounts[0].name);
|
||||
checkDrivePermission(accounts[0].name);
|
||||
checkDrivePermission();
|
||||
return;
|
||||
}
|
||||
|
||||
String googleAccount = PreferencesUtils.getString(this, R.string.google_account_key,
|
||||
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
String googleAccount = PreferencesUtils.getString(
|
||||
this, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
if (accounts[i].name.equals(googleAccount)) {
|
||||
sendRequest.setAccount(accounts[i]);
|
||||
checkDrivePermission(accounts[i].name);
|
||||
checkDrivePermission();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -95,39 +93,43 @@ public class AccountChooserActivity extends Activity {
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case SyncUtils.DRIVE_PERMISSION_REQUEST_CODE:
|
||||
SyncUtils.cancelNotification(this);
|
||||
case SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE:
|
||||
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
driveCallback.onSuccess();
|
||||
} else {
|
||||
driveCallback.onFailure();
|
||||
}
|
||||
break;
|
||||
case SendToGoogleUtils.FUSION_TABLES_PERMISSION_REQUEST_CODE:
|
||||
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.FUSION_TABLES_NOTIFICATION_ID);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
fusionTablesCallback.onSuccess();
|
||||
} else {
|
||||
fusionTablesCallback.onFailure();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_NO_ACCOUNT_ID:
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
return new AlertDialog.Builder(this).setCancelable(true)
|
||||
.setMessage(R.string.send_google_no_account_message)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.send_google_no_account_title)
|
||||
.create();
|
||||
}).setTitle(R.string.send_google_no_account_title).create();
|
||||
case DIALOG_CHOOSER_ID:
|
||||
return createChooserDialog();
|
||||
default:
|
||||
@@ -143,103 +145,115 @@ public class AccountChooserActivity extends Activity {
|
||||
for (int i = 0; i < accounts.length; i++) {
|
||||
choices[i] = accounts[i].name;
|
||||
}
|
||||
return new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
return new AlertDialog.Builder(this).setCancelable(true)
|
||||
.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
Account account = accounts[position];
|
||||
PreferencesUtils.setString(
|
||||
AccountChooserActivity.this, R.string.google_account_key, account.name);
|
||||
sendRequest.setAccount(account);
|
||||
checkDrivePermission(account.name);
|
||||
checkDrivePermission();
|
||||
}
|
||||
})
|
||||
.setSingleChoiceItems(choices, 0, null)
|
||||
.setTitle(R.string.send_google_choose_account_title)
|
||||
.create();
|
||||
}).setSingleChoiceItems(choices, 0, null)
|
||||
.setTitle(R.string.send_google_choose_account_title).create();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the Drive permission.
|
||||
*
|
||||
* @param accountName the account name
|
||||
*/
|
||||
private void checkDrivePermission(String accountName) {
|
||||
private void checkDrivePermission() {
|
||||
if (sendRequest.isSendDrive()) {
|
||||
SyncUtils.checkPermissionByActivity(this, accountName, driveCallback);
|
||||
SendToGoogleUtils.checkPermissionByActivity(this, sendRequest.getAccount().name,
|
||||
SendToGoogleUtils.DRIVE_SCOPE, SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE,
|
||||
driveCallback);
|
||||
} else {
|
||||
driveCallback.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the Fusion Tables permission.
|
||||
*/
|
||||
private void checkFusionTablesPermission() {
|
||||
if (sendRequest.isSendFusionTables()) {
|
||||
SendToGoogleUtils.checkPermissionByActivity(this, sendRequest.getAccount().name,
|
||||
SendToGoogleUtils.FUSION_TABLES_SCOPE,
|
||||
SendToGoogleUtils.FUSION_TABLES_PERMISSION_REQUEST_CODE, fusionTablesCallback);
|
||||
} else {
|
||||
fusionTablesCallback.onSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
private PermissionCallback spreadsheetsCallback = new PermissionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
startNextActivity();
|
||||
}
|
||||
@Override
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
handleNoAccountPermission();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private PermissionCallback docsCallback = new PermissionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
getPermission(SpreadsheetsClient.SERVICE, sendRequest.isSendDocs(), spreadsheetsCallback);
|
||||
}
|
||||
@Override
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
handleNoAccountPermission();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private PermissionCallback fusionTablesCallback = new PermissionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
getPermission(DocumentsClient.SERVICE, sendRequest.isSendDocs(), docsCallback);
|
||||
}
|
||||
@Override
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
handleNoAccountPermission();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private PermissionCallback mapsCallback = new PermissionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
getPermission(
|
||||
SendFusionTablesUtils.SERVICE, sendRequest.isSendFusionTables(), fusionTablesCallback);
|
||||
checkFusionTablesPermission();
|
||||
}
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
handleNoAccountPermission();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private PermissionCallback driveCallback = new PermissionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
getPermission(MapsConstants.SERVICE_NAME, sendRequest.isSendMaps(), mapsCallback);
|
||||
}
|
||||
@Override
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
handleNoAccountPermission();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the user permission to access a service.
|
||||
*
|
||||
@@ -252,7 +266,7 @@ public class AccountChooserActivity extends Activity {
|
||||
if (needPermission) {
|
||||
AccountManager.get(this).getAuthToken(sendRequest.getAccount(), authTokenType, null, this,
|
||||
new AccountManagerCallback<Bundle>() {
|
||||
@Override
|
||||
@Override
|
||||
public void run(AccountManagerFuture<Bundle> future) {
|
||||
try {
|
||||
if (future.getResult().getString(AccountManager.KEY_AUTHTOKEN) != null) {
|
||||
@@ -290,7 +304,6 @@ public class AccountChooserActivity extends Activity {
|
||||
* !sendMaps && !sendFusionTables && sendDocs -> {@link SendDocsActivity}
|
||||
* <p>
|
||||
* !sendMaps && !sendFusionTables && !sendDocs -> {@link UploadResultActivity}
|
||||
*
|
||||
*/
|
||||
private void startNextActivity() {
|
||||
Class<?> next;
|
||||
@@ -310,7 +323,7 @@ public class AccountChooserActivity extends Activity {
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles when not able to get account permission.
|
||||
*/
|
||||
|
||||
@@ -19,11 +19,25 @@ package com.google.android.apps.mytracks.io.sendtogoogle;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
||||
import com.google.api.services.drive.DriveScopes;
|
||||
import com.google.api.services.fusiontables.FusiontablesScopes;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -36,6 +50,16 @@ public class SendToGoogleUtils {
|
||||
|
||||
private static final String TAG = SendToGoogleUtils.class.getSimpleName();
|
||||
|
||||
// Request code to obtain Drive permission
|
||||
public static final int DRIVE_PERMISSION_REQUEST_CODE = 1;
|
||||
public static final String DRIVE_SCOPE = DriveScopes.DRIVE;
|
||||
public static final int DRIVE_NOTIFICATION_ID = 1;
|
||||
|
||||
// Request code to obtain Fusion Tables permission
|
||||
public static final int FUSION_TABLES_PERMISSION_REQUEST_CODE = 2;
|
||||
public static final String FUSION_TABLES_SCOPE = FusiontablesScopes.FUSIONTABLES;
|
||||
public static final int FUSION_TABLES_NOTIFICATION_ID = 2;
|
||||
|
||||
private SendToGoogleUtils() {}
|
||||
|
||||
/**
|
||||
@@ -115,4 +139,105 @@ public class SendToGoogleUtils {
|
||||
splitTracks.add(segment);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the google account credential.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param scope the scope
|
||||
*/
|
||||
public static GoogleAccountCredential getGoogleAccountCredential(
|
||||
Context context, String accountName, String scope) throws IOException, GoogleAuthException {
|
||||
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context, scope);
|
||||
credential.setSelectedAccountName(accountName);
|
||||
credential.getToken();
|
||||
return credential;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks permission by an activity. Will start an activity to request
|
||||
* permission using the request code.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param accountName the account name
|
||||
* @param scope the scope
|
||||
* @param requestCode the request code
|
||||
* @param permissionCallback the permission callback
|
||||
*/
|
||||
public static void checkPermissionByActivity(final Activity activity, final String accountName,
|
||||
final String scope, final int requestCode, final PermissionCallback permissionCallback) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getGoogleAccountCredential(activity, accountName, scope);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onSuccess();
|
||||
}
|
||||
});
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
activity.startActivityForResult(e.getIntent(), requestCode);
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onFailure();
|
||||
}
|
||||
});
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
activity.startActivityForResult(e.getIntent(), requestCode);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onFailure();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a notification to request permission.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param intent the intent
|
||||
* @param notificaitonId the notification id
|
||||
*/
|
||||
public static void sendNotification(
|
||||
Context context, String accountName, Intent intent, int notificaitonId) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).addFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentText(context.getString(R.string.permission_request_message, accountName))
|
||||
.setContentTitle(context.getString(R.string.permission_request_title))
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTicker(context.getString(R.string.permission_request_title));
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(notificaitonId, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any notification to request a permission.
|
||||
*
|
||||
* @param context the context
|
||||
* @param notificationId the notification id
|
||||
*/
|
||||
public static void cancelNotification(Context context, int notificationId) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.io.sync;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.file.KmlImporter;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
@@ -101,8 +102,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
}
|
||||
|
||||
try {
|
||||
GoogleAccountCredential credential = SyncUtils.getGoogleAccountCredential(
|
||||
context, account.name);
|
||||
GoogleAccountCredential credential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
context, account.name, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
if (credential == null) {
|
||||
return;
|
||||
}
|
||||
@@ -125,11 +126,13 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
}
|
||||
insertNewTracks(folderId);
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
SendToGoogleUtils.sendNotification(
|
||||
context, account.name, e.getIntent(), SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
}
|
||||
|
||||
@@ -22,11 +22,8 @@ import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
||||
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
|
||||
import com.google.android.apps.mytracks.io.file.TrackWriter;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.PermissionCallback;
|
||||
import com.google.android.apps.mytracks.util.FileUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.gms.auth.UserRecoverableAuthException;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.extensions.android.http.AndroidHttp;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||
@@ -35,7 +32,6 @@ import com.google.api.client.http.FileContent;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.services.drive.Drive;
|
||||
import com.google.api.services.drive.Drive.Files.List;
|
||||
import com.google.api.services.drive.DriveScopes;
|
||||
import com.google.api.services.drive.model.File;
|
||||
import com.google.api.services.drive.model.FileList;
|
||||
import com.google.api.services.drive.model.ParentReference;
|
||||
@@ -43,14 +39,9 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
@@ -70,9 +61,6 @@ import java.util.Locale;
|
||||
*/
|
||||
public class SyncUtils {
|
||||
|
||||
// Request code to obtain Drive permission
|
||||
public static final int DRIVE_PERMISSION_REQUEST_CODE = 1;
|
||||
|
||||
// Get tracks with drive id
|
||||
public static final String DRIVE_ID_TRACKS_QUERY = TracksColumns.DRIVEID + " IS NOT NULL AND "
|
||||
+ TracksColumns.DRIVEID + "!=''";
|
||||
@@ -103,106 +91,9 @@ public class SyncUtils {
|
||||
|
||||
private static final String TAG = SyncUtils.class.getSimpleName();
|
||||
private static final String SYNC_AUTHORITY = "com.google.android.maps.mytracks";
|
||||
private static final int NOTIFICATION_ID = 0;
|
||||
|
||||
private SyncUtils() {}
|
||||
|
||||
/**
|
||||
* Gets the google account credential.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
*/
|
||||
public static GoogleAccountCredential getGoogleAccountCredential(
|
||||
Context context, String accountName) throws IOException, GoogleAuthException {
|
||||
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(
|
||||
context, DriveScopes.DRIVE);
|
||||
credential.setSelectedAccountName(accountName);
|
||||
credential.getToken();
|
||||
return credential;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks permission by an activity. Will start an activity to request
|
||||
* permission with request code {@link #DRIVE_PERMISSION_REQUEST_CODE}.
|
||||
*
|
||||
* @param activity the activity
|
||||
* @param accountName the account name
|
||||
* @param permissionCallback the permission callback
|
||||
*/
|
||||
public static void checkPermissionByActivity(final Activity activity, final String accountName,
|
||||
final PermissionCallback permissionCallback) {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
getGoogleAccountCredential(activity, accountName);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onSuccess();
|
||||
}
|
||||
});
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
activity.startActivityForResult(e.getIntent(), DRIVE_PERMISSION_REQUEST_CODE);
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onFailure();
|
||||
}
|
||||
});
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
activity.startActivityForResult(e.getIntent(), DRIVE_PERMISSION_REQUEST_CODE);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
permissionCallback.onFailure();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a notification to request permission.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param intent the intent
|
||||
*/
|
||||
public static void sendNotification(Context context, String accountName, Intent intent) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).addFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setAutoCancel(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentText(context.getString(R.string.permission_request_message, accountName))
|
||||
.setContentTitle(context.getString(R.string.permission_request_title))
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTicker(context.getString(R.string.permission_request_title));
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any notification to request drive permission.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static void cancelNotification(Context context) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
|
||||
Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs now for the current account.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.PermissionCallback;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
@@ -143,8 +144,8 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity {
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case SyncUtils.DRIVE_PERMISSION_REQUEST_CODE:
|
||||
SyncUtils.cancelNotification(this);
|
||||
case SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE:
|
||||
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
permissionCallback.onSuccess();
|
||||
} else {
|
||||
@@ -173,8 +174,9 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity {
|
||||
String googleAccount = PreferencesUtils.getString(
|
||||
GoogleSettingsActivity.this, R.string.google_account_key,
|
||||
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
SyncUtils.checkPermissionByActivity(
|
||||
GoogleSettingsActivity.this, googleAccount, permissionCallback);
|
||||
SendToGoogleUtils.checkPermissionByActivity(GoogleSettingsActivity.this,
|
||||
googleAccount, SendToGoogleUtils.DRIVE_SCOPE,
|
||||
SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE, permissionCallback);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
-60
@@ -20,8 +20,6 @@ import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
@@ -225,62 +223,4 @@ public class SendFusionTablesUtilsTest extends TestCase {
|
||||
SendFusionTablesUtils.appendLocation(location, builder);
|
||||
assertEquals("10.1,20.2,30.3", builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with a null
|
||||
* inputstream.
|
||||
*/
|
||||
public void testGetTableId_null() {
|
||||
assertEquals(null, SendFusionTablesUtils.getTableId(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with an
|
||||
* inputstream containing no data.
|
||||
*/
|
||||
public void testGetTableId_no_data() {
|
||||
InputStream inputStream = new ByteArrayInputStream(new byte[0]);
|
||||
assertEquals(null, SendFusionTablesUtils.getTableId(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with an empty
|
||||
* inputstream.
|
||||
*/
|
||||
public void testGetTableId_empty() {
|
||||
String string = "";
|
||||
InputStream inputStream = new ByteArrayInputStream(string.getBytes());
|
||||
assertEquals(null, SendFusionTablesUtils.getTableId(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with an one
|
||||
* line inputstream.
|
||||
*/
|
||||
public void testGetTableId_one_line() {
|
||||
String string = "tableid";
|
||||
InputStream inputStream = new ByteArrayInputStream(string.getBytes());
|
||||
assertEquals(null, SendFusionTablesUtils.getTableId(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with an
|
||||
* inputstream not containing "tableid".
|
||||
*/
|
||||
public void testGetTableId_no_table_id() {
|
||||
String string = "error\n123";
|
||||
InputStream inputStream = new ByteArrayInputStream(string.getBytes());
|
||||
assertEquals(null, SendFusionTablesUtils.getTableId(inputStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SendFusionTablesUtils#getTableId(InputStream)} with a valid
|
||||
* inputstream.
|
||||
*/
|
||||
public void testGetTableId() {
|
||||
String string = "tableid\n123";
|
||||
InputStream inputStream = null;
|
||||
inputStream = new ByteArrayInputStream(string.getBytes());
|
||||
assertEquals("123", SendFusionTablesUtils.getTableId(inputStream));
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -17,7 +17,6 @@ package com.google.android.apps.mytracks.endtoendtest;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.io.docs.SendDocsUtils;
|
||||
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesAsyncTask;
|
||||
import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesUtils;
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataClientFactory;
|
||||
import com.google.android.apps.mytracks.io.gdata.docs.DocumentsClient;
|
||||
@@ -63,11 +62,17 @@ import java.util.Locale;
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class GoogleUtils {
|
||||
|
||||
public static final String DOCUMENT_NAME_PREFIX = "My Tracks";
|
||||
public static final String ACCOUNT_NAME_1 = "mytrackstest@gmail.com";
|
||||
public static final String ACCOUNT_NAME_2 = "mytrackstest2@gmail.com";
|
||||
public static final String SPREADSHEET_NAME = DOCUMENT_NAME_PREFIX + "-" + EndToEndTestUtils.activityType;
|
||||
|
||||
private static final String APP_NAME_PREFIX = "Google-MyTracks-";
|
||||
private static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
private static final String FUSION_TABLES_BASE_URL = "https://www.google.com/fusiontables/api/query";
|
||||
private static final String GDATA_VERSION = "2";
|
||||
|
||||
/**
|
||||
* Gets the account to access Google Services.
|
||||
*
|
||||
@@ -408,7 +413,7 @@ public class GoogleUtils {
|
||||
String fusionTableAuthToken = AccountManager.get(context).blockingGetAuthToken(getAccount(context),
|
||||
SendFusionTablesUtils.SERVICE, false);
|
||||
|
||||
GenericUrl url = new GenericUrl(SendFusionTablesAsyncTask.FUSION_TABLES_BASE_URL);
|
||||
GenericUrl url = new GenericUrl(FUSION_TABLES_BASE_URL);
|
||||
String sql = "sql=" + query;
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(sql.getBytes());
|
||||
InputStreamContent inputStreamContent = new InputStreamContent(null, inputStream);
|
||||
@@ -418,11 +423,11 @@ public class GoogleUtils {
|
||||
.createRequestFactory(new MethodOverride())).buildPostRequest(url, inputStreamContent);
|
||||
|
||||
GoogleHeaders headers = new GoogleHeaders();
|
||||
headers.setApplicationName(SendFusionTablesAsyncTask.APP_NAME_PREFIX
|
||||
headers.setApplicationName(APP_NAME_PREFIX
|
||||
+ SystemUtils.getMyTracksVersion(context));
|
||||
headers.setGDataVersion(SendFusionTablesAsyncTask.GDATA_VERSION);
|
||||
headers.setGDataVersion(GDATA_VERSION);
|
||||
headers.setGoogleLogin(fusionTableAuthToken);
|
||||
headers.setContentType(SendFusionTablesAsyncTask.CONTENT_TYPE);
|
||||
headers.setContentType(CONTENT_TYPE);
|
||||
request.setHeaders(headers);
|
||||
|
||||
HttpResponse response;
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ package com.google.android.apps.mytracks.endtoendtest.sync;
|
||||
import com.google.android.apps.mytracks.TrackListActivity;
|
||||
import com.google.android.apps.mytracks.endtoendtest.EndToEndTestUtils;
|
||||
import com.google.android.apps.mytracks.endtoendtest.GoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
@@ -119,8 +120,8 @@ public class SyncTestUtils {
|
||||
public static Drive getGoogleDrive(Context context) throws IOException, GoogleAuthException {
|
||||
String googleAccount = PreferencesUtils.getString(
|
||||
context, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
GoogleAccountCredential credential = SyncUtils.getGoogleAccountCredential(
|
||||
context, googleAccount);
|
||||
GoogleAccountCredential credential = SendToGoogleUtils.getGoogleAccountCredential(
|
||||
context, googleAccount, SendToGoogleUtils.DRIVE_SCOPE);
|
||||
return SyncUtils.getDriveService(credential);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user