/*
* Copyright 2012 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.spreadsheets;
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.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
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.auth.oauth2.BearerToken;
import com.google.api.client.auth.oauth2.Credential;
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.http.ByteArrayContent;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.common.annotations.VisibleForTesting;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.spreadsheet.ListEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;
import com.google.gdata.util.ServiceException;
import android.accounts.Account;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.Locale;
/**
* AsyncTask to send a track to Google Spreadsheet.
*
* @author Jimmy Shih
*/
public class SendSpreadsheetsAsyncTask extends AbstractSendAsyncTask {
private static final String TAG = SendSpreadsheetsAsyncTask.class.getSimpleName();
private static final String
GOOGLE_SPREADSHEET_MIME_TYPE = "application/vnd.google-apps.spreadsheet";
private static final String
OPENDOCUMENT_SPREADSHEET_MIME_TYPE = "application/x-vnd.oasis.opendocument.spreadsheet";
@VisibleForTesting
public static final String GET_SPREADSHEET_QUERY =
"'root' in parents and title = '%s' and mimeType = '" + GOOGLE_SPREADSHEET_MIME_TYPE
+ "' and trashed = false";
@VisibleForTesting
public static final String
GET_WORKSHEETS_URI = "https://spreadsheets.google.com/feeds/worksheets/%s/private/full";
private static final int PROGRESS_GET_SPREADSHEET_ID = 0;
private static final int PROGRESS_GET_WORKSHEET_URL = 35;
private static final int PROGRESS_ADD_TRACK_INFO = 70;
private static final int PROGRESS_COMPLETE = 100;
private final long trackId;
private final Account account;
private final Context context;
private final MyTracksProviderUtils myTracksProviderUtils;
public SendSpreadsheetsAsyncTask(SendSpreadsheetsActivity activity, long trackId, Account account) {
super(activity);
this.trackId = trackId;
this.account = account;
context = activity.getApplicationContext();
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
}
@Override
protected void closeConnection() {}
@Override
protected boolean performTask() {
try {
SpreadsheetService spreadsheetService = new SpreadsheetService(
"MyTracks-" + SystemUtils.getMyTracksVersion(context));
Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod());
credential.setAccessToken(
SendToGoogleUtils.getToken(context, account.name, SendToGoogleUtils.SPREADSHEETS_SCOPE));
spreadsheetService.setOAuth2Credentials(credential);
Track track = myTracksProviderUtils.getTrack(trackId);
if (track == null) {
Log.d(TAG, "No track for " + trackId);
return false;
}
String title = context.getString(R.string.my_tracks_app_name);
if (track.getCategory() != null && !track.getCategory().equals("")) {
title += "-" + track.getCategory();
}
publishProgress(PROGRESS_GET_SPREADSHEET_ID);
String spreadsheetId = getSpreadSheetId(title);
if (spreadsheetId == null) {
Log.d(TAG, "Unable to get the spreadsheet ID for " + title);
return false;
}
publishProgress(PROGRESS_GET_WORKSHEET_URL);
URL worksheetUrl = getWorksheetUrl(spreadsheetService, spreadsheetId);
if (worksheetUrl == null) {
Log.d(TAG, "Unable to get the worksheet url for " + spreadsheetId);
return false;
}
publishProgress(PROGRESS_ADD_TRACK_INFO);
if (!addTrackInfo(spreadsheetService, worksheetUrl, track)) {
Log.d(TAG, "Unable to add track info");
return false;
}
publishProgress(PROGRESS_COMPLETE);
return true;
} catch (UserRecoverableAuthException e) {
SendToGoogleUtils.sendNotification(
context, account.name, e.getIntent(), SendToGoogleUtils.SPREADSHEETS_NOTIFICATION_ID);
return false;
} catch (GoogleAuthException e) {
Log.e(TAG, "GoogleaAuthException", e);
return retryTask();
} catch (UserRecoverableAuthIOException e) {
SendToGoogleUtils.sendNotification(
context, account.name, e.getIntent(), SendToGoogleUtils.SPREADSHEETS_NOTIFICATION_ID);
return false;
} catch (IOException e) {
Log.e(TAG, "IOException", e);
return retryTask();
} catch (ServiceException e) {
Log.e(TAG, "ServiceException", e);
return retryTask();
}
}
@Override
protected void invalidateToken() {}
/**
* Gets the spreadsheet id.
*
* @param fileName the file name
*/
private String getSpreadSheetId(String fileName) throws IOException, GoogleAuthException {
if (isCancelled()) {
return null;
}
GoogleAccountCredential driveCredential = SendToGoogleUtils.getGoogleAccountCredential(
context, account.name, SendToGoogleUtils.DRIVE_SCOPE);
if (driveCredential == null) {
return null;
}
Drive drive = SyncUtils.getDriveService(driveCredential);
com.google.api.services.drive.Drive.Files.List list = drive.files()
.list().setQ(String.format(Locale.US, GET_SPREADSHEET_QUERY, fileName));
FileList result = list.execute();
for (File file : result.getItems()) {
if (file.getSharedWithMeDate() == null) {
return file.getId();
}
}
if (isCancelled()) {
return null;
}
InputStream inputStream = null;
try {
inputStream = context.getResources().openRawResource(R.raw.mytracks_empty_spreadsheet);
byte[] b = new byte[inputStream.available()];
inputStream.read(b);
ByteArrayContent fileContent = new ByteArrayContent(OPENDOCUMENT_SPREADSHEET_MIME_TYPE, b);
File file = new File();
file.setTitle(fileName);
file.setMimeType(OPENDOCUMENT_SPREADSHEET_MIME_TYPE);
return drive.files().insert(file, fileContent).setConvert(true).execute().getId();
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
/**
* Gets the worksheet url.
*
* @param spreadsheetService the spreadsheet service
* @param spreadsheetId the spreadsheet id
*/
private URL getWorksheetUrl(SpreadsheetService spreadsheetService, String spreadsheetId)
throws IOException, ServiceException {
if (isCancelled()) {
return null;
}
URL url = new URL(String.format(Locale.US, GET_WORKSHEETS_URI, spreadsheetId));
WorksheetFeed feed = spreadsheetService.getFeed(url, WorksheetFeed.class);
List<WorksheetEntry> worksheets = feed.getEntries();
if (worksheets.size() > 0) {
return worksheets.get(0).getListFeedUrl();
}
return null;
}
/**
* Adds track info to a worksheet.
*
* @param spreadsheetService the spreadsheet service
* @param worksheetUrl the worksheet url
* @param track the track
* @return true if completes.
*/
private boolean addTrackInfo(SpreadsheetService spreadsheetService, URL worksheetUrl, Track track)
throws IOException, ServiceException {
if (isCancelled()) {
return false;
}
TripStatistics tripStatistics = track.getTripStatistics();
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
String distanceUnit = context.getString(
metricUnits ? R.string.unit_kilometer : R.string.unit_mile);
String speedUnit = context.getString(
metricUnits ? R.string.unit_kilometer_per_hour : R.string.unit_mile_per_hour);
String elevationUnit = context.getString(
metricUnits ? R.string.unit_meter : R.string.unit_feet);
ListEntry row = new ListEntry();
row.getCustomElements().setValueLocal("name", track.getName());
row.getCustomElements().setValueLocal("description", track.getDescription());
row.getCustomElements()
.setValueLocal("date", StringUtils.formatDateTime(context, tripStatistics.getStartTime()));
row.getCustomElements().setValueLocal(
"totaltime", StringUtils.formatElapsedTimeWithHour(tripStatistics.getTotalTime()));
row.getCustomElements().setValueLocal(
"movingtime", StringUtils.formatElapsedTimeWithHour(tripStatistics.getMovingTime()));
row.getCustomElements().setValueLocal(
"distance", SendSpreadsheetsUtils.getDistance(tripStatistics.getTotalDistance(), metricUnits));
row.getCustomElements().setValueLocal("distanceunit", distanceUnit);
row.getCustomElements().setValueLocal(
"averagespeed", SendSpreadsheetsUtils.getSpeed(tripStatistics.getAverageSpeed(), metricUnits));
row.getCustomElements().setValueLocal("averagemovingspeed",
SendSpreadsheetsUtils.getSpeed(tripStatistics.getAverageMovingSpeed(), metricUnits));
row.getCustomElements().setValueLocal(
"maxspeed", SendSpreadsheetsUtils.getSpeed(tripStatistics.getMaxSpeed(), metricUnits));
row.getCustomElements().setValueLocal("speedunit", speedUnit);
row.getCustomElements().setValueLocal("elevationgain",
SendSpreadsheetsUtils.getElevation(tripStatistics.getTotalElevationGain(), metricUnits));
row.getCustomElements().setValueLocal(
"minelevation", SendSpreadsheetsUtils.getElevation(tripStatistics.getMinElevation(), metricUnits));
row.getCustomElements().setValueLocal(
"maxelevation", SendSpreadsheetsUtils.getElevation(tripStatistics.getMaxElevation(), metricUnits));
row.getCustomElements().setValueLocal("elevationunit", elevationUnit);
ListEntry result = spreadsheetService.insert(worksheetUrl, row);
return result != null;
}
}