forked from upstream-mirrors/OpenTracks
Add retry for SyncAdapter.
This commit is contained in:
@@ -84,8 +84,8 @@ public class SendDriveAsyncTask extends AbstractSendAsyncTask {
|
||||
String driveId = track.getDriveId();
|
||||
if (driveId != null && !driveId.equals("")) {
|
||||
File driveFile = drive.files().get(driveId).execute();
|
||||
if (SyncUtils.isValid(driveFile, folderId)
|
||||
&& SyncUtils.updateDriveFile(context, myTracksProviderUtils, drive, driveFile, track)) {
|
||||
if (SyncUtils.isValid(driveFile, folderId) && SyncUtils.updateDriveFile(
|
||||
drive, driveFile, context, myTracksProviderUtils, track, false)) {
|
||||
addPermission(drive, driveId);
|
||||
return true;
|
||||
}
|
||||
@@ -95,7 +95,8 @@ public class SendDriveAsyncTask extends AbstractSendAsyncTask {
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
}
|
||||
|
||||
String id = SyncUtils.addDriveFile(context, myTracksProviderUtils, drive, folderId, track);
|
||||
String id = SyncUtils.insertDriveFile(
|
||||
drive, folderId, context, myTracksProviderUtils, track, false);
|
||||
if (id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
@@ -174,7 +175,8 @@ abstract class AbstractImporter extends DefaultHandler {
|
||||
* @param inputStream the input stream.
|
||||
* @return an array of imported track ids.
|
||||
*/
|
||||
public long[] importFile(InputStream inputStream) throws Exception {
|
||||
public long[] importFile(InputStream inputStream)
|
||||
throws SAXException, IOException, ParserConfigurationException {
|
||||
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
|
||||
SAXParser saxParser = saxParserFactory.newSAXParser();
|
||||
|
||||
@@ -186,9 +188,11 @@ abstract class AbstractImporter extends DefaultHandler {
|
||||
long end = System.currentTimeMillis();
|
||||
Log.d(Constants.TAG, "Total import time: " + (end - start) + "ms");
|
||||
} catch (SAXException e) {
|
||||
handleException(e);
|
||||
handleException();
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
handleException(e);
|
||||
handleException();
|
||||
throw e;
|
||||
}
|
||||
long[] result = new long[trackIds.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
@@ -578,13 +582,10 @@ abstract class AbstractImporter extends DefaultHandler {
|
||||
|
||||
/**
|
||||
* Handles an exception.
|
||||
*
|
||||
* @param e the exception
|
||||
*/
|
||||
private void handleException(Exception e) throws Exception {
|
||||
private void handleException() {
|
||||
for (long trackId : trackIds) {
|
||||
myTracksProviderUtils.deleteTrack(trackId);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@ 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.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.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.services.drive.Drive;
|
||||
@@ -40,7 +43,6 @@ import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -51,6 +53,10 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* SyncAdapter to sync tracks with Google Drive.
|
||||
*
|
||||
@@ -94,17 +100,18 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
GoogleAccountCredential credential = SyncUtils.getCredential(context, account.name);
|
||||
if (credential == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drive == null || !driveAccountName.equals(account.name)) {
|
||||
drive = SyncUtils.getDriveService(credential);
|
||||
driveAccountName = account.name;
|
||||
}
|
||||
|
||||
try {
|
||||
GoogleAccountCredential credential = SyncUtils.getGoogleAccountCredential(
|
||||
context, account.name);
|
||||
if (credential == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drive == null || !driveAccountName.equals(account.name)) {
|
||||
drive = SyncUtils.getDriveService(credential);
|
||||
driveAccountName = account.name;
|
||||
}
|
||||
|
||||
String folderId = SyncUtils.getMyTracksFolder(context, drive);
|
||||
if (folderId == null) {
|
||||
return;
|
||||
@@ -117,8 +124,14 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
performIncrementalSync(folderId, largestChangeId);
|
||||
}
|
||||
insertNewTracks(folderId);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception", e);
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
SyncUtils.sendNotification(context, account.name, e.getIntent());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,24 +140,45 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
*
|
||||
* @param folderId the folder id
|
||||
*/
|
||||
private void performInitialSync(String folderId) throws Exception {
|
||||
private void performInitialSync(String folderId) throws IOException {
|
||||
|
||||
// Get the largest change id first to avoid race conditions
|
||||
About about = drive.about().get().setFields(ABOUT_GET_FIELDS).execute();
|
||||
long largestChangeId = about.getLargestChangeId();
|
||||
|
||||
// Get all the KML files in the "My Drive:/My Tracks" folder
|
||||
Files.List request = drive.files().list()
|
||||
.setQ(String.format(Locale.US, SyncUtils.MY_TRACKS_FOLDER_FILES_QUERY, folderId));
|
||||
Map<String, File> idToFileMap = getFiles(request);
|
||||
insertNewDriveFiles(idToFileMap.values());
|
||||
Files.List myTracksFolderRequest = drive.files()
|
||||
.list().setQ(String.format(Locale.US, SyncUtils.MY_TRACKS_FOLDER_FILES_QUERY, folderId));
|
||||
Map<String, File> myTracksFolderMap = getFiles(myTracksFolderRequest);
|
||||
|
||||
// Get all the KML files in the "Shared with me:/" folder
|
||||
request = drive.files().list().setQ(SyncUtils.SHARED_WITH_ME_FILES_QUERY);
|
||||
idToFileMap = getFiles(request);
|
||||
insertNewDriveFiles(idToFileMap.values());
|
||||
Files.List sharedWithMeRequest = drive.files()
|
||||
.list().setQ(SyncUtils.SHARED_WITH_ME_FILES_QUERY);
|
||||
Map<String, File> sharedWithMeMap = getFiles(sharedWithMeRequest);
|
||||
|
||||
PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, largestChangeId);
|
||||
try {
|
||||
insertNewDriveFiles(myTracksFolderMap.values());
|
||||
insertNewDriveFiles(sharedWithMeMap.values());
|
||||
PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, largestChangeId);
|
||||
} catch (IOException e) {
|
||||
|
||||
// Remove all imported tracks
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = myTracksProviderUtils.getTrackCursor(SyncUtils.DRIVE_ID_TRACKS_QUERY, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
Track track = myTracksProviderUtils.createTrack(cursor);
|
||||
myTracksProviderUtils.deleteTrack(track.getId());
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,26 +187,13 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
* @param folderId the folder id
|
||||
* @param largestChangeId the largest change id
|
||||
*/
|
||||
private void performIncrementalSync(String folderId, long largestChangeId) throws Exception {
|
||||
private void performIncrementalSync(String folderId, long largestChangeId) throws IOException {
|
||||
|
||||
// Move to trash in Drive all deleted tracks
|
||||
String driveDeletedList = PreferencesUtils.getString(
|
||||
context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT);
|
||||
String deletedIds[] = TextUtils.split(driveDeletedList, ";");
|
||||
for (String id : deletedIds) {
|
||||
try {
|
||||
File driveFile = drive.files().get(id).execute();
|
||||
if (SyncUtils.isInFolder(driveFile, folderId)) {
|
||||
if (!driveFile.getLabels().getTrashed()) {
|
||||
drive.files().trash(id).execute();
|
||||
}
|
||||
// if trashed, ignore
|
||||
} else if (SyncUtils.isSharedWithMe(driveFile)) {
|
||||
drive.files().delete(id).execute();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// safe to ignore
|
||||
}
|
||||
for (String driveId : deletedIds) {
|
||||
deleteDriveFile(driveId, folderId, true);
|
||||
}
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT);
|
||||
@@ -197,7 +218,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
Log.d(TAG, "Delete local track " + track.getName());
|
||||
myTracksProviderUtils.deleteTrack(track.getId());
|
||||
} else {
|
||||
mergeFiles(track, driveFile);
|
||||
merge(track, driveFile);
|
||||
}
|
||||
changes.remove(driveId);
|
||||
} else {
|
||||
@@ -206,7 +227,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
// Handle the case the track has changed
|
||||
File driveFile = drive.files().get(driveId).execute();
|
||||
if (SyncUtils.isValid(driveFile, folderId)) {
|
||||
mergeFiles(track, driveFile);
|
||||
merge(track, driveFile);
|
||||
} else {
|
||||
/*
|
||||
* Track has a drive id, but the drive id is no longer valid.
|
||||
@@ -225,10 +246,6 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
// Handle new drive files
|
||||
insertNewDriveFiles(changes.values());
|
||||
PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, largestChangeId);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
@@ -253,8 +270,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
if (track.getId() == recordingTrackId) {
|
||||
continue;
|
||||
}
|
||||
// Note, will retry on the next sync if unable to add drive file
|
||||
SyncUtils.addDriveFile(context, myTracksProviderUtils, drive, folderId, track);
|
||||
// If not successful, the next sync will retry again
|
||||
SyncUtils.insertDriveFile(drive, folderId, context, myTracksProviderUtils, track, true);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} finally {
|
||||
@@ -268,24 +285,36 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
* Inserts new Google Drive files.
|
||||
*
|
||||
* @param driveFiles a collection of drive files to insert
|
||||
* @throws IOException
|
||||
*/
|
||||
private void insertNewDriveFiles(Collection<File> driveFiles) throws Exception {
|
||||
private void insertNewDriveFiles(Collection<File> driveFiles) throws IOException {
|
||||
for (File driveFile : driveFiles) {
|
||||
if (driveFile != null) {
|
||||
InputStream inputStream = downloadDriveFile(driveFile);
|
||||
// TODO: should retry if inputStream is null
|
||||
InputStream inputStream = downloadDriveFile(driveFile, true);
|
||||
if (inputStream != null) {
|
||||
KmlImporter kmlImporter = new KmlImporter(context, -1L);
|
||||
long[] tracksIds = kmlImporter.importFile(inputStream);
|
||||
if (tracksIds.length == 1) {
|
||||
Track track = myTracksProviderUtils.getTrack(tracksIds[0]);
|
||||
track.setDriveId(driveFile.getId());
|
||||
track.setModifiedTime(driveFile.getModifiedDate().getValue());
|
||||
track.setSharedWithMe(driveFile.getSharedWithMeDate() != null);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
Log.d(TAG, "Add from Google Drive " + track.getName());
|
||||
try {
|
||||
long[] trackIds = kmlImporter.importFile(inputStream);
|
||||
if (trackIds.length == 1) {
|
||||
Track track = myTracksProviderUtils.getTrack(trackIds[0]);
|
||||
track.setDriveId(driveFile.getId());
|
||||
track.setModifiedTime(driveFile.getModifiedDate().getValue());
|
||||
track.setSharedWithMe(driveFile.getSharedWithMeDate() != null);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
Log.d(TAG, "Add from Google Drive " + track.getName());
|
||||
} else {
|
||||
// Clean up if imported more than one track
|
||||
for (int i = 0; i < trackIds.length; i++) {
|
||||
myTracksProviderUtils.deleteTrack(trackIds[i]);
|
||||
}
|
||||
}
|
||||
} catch (SAXException e) {
|
||||
Log.e(TAG, "Unable to insert new drive file for " + driveFile.getId(), e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to insert new drive file for " + driveFile.getId(), e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
Log.e(TAG, "Unable to insert new drive file for " + driveFile.getId(), e);
|
||||
}
|
||||
// Ignore if tracksId.length != 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,20 +326,15 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
* @param request the request
|
||||
* @return a map of file id to file
|
||||
*/
|
||||
private Map<String, File> getFiles(Files.List request) {
|
||||
private Map<String, File> getFiles(Files.List request) throws IOException {
|
||||
Map<String, File> idToFileMap = new HashMap<String, File>();
|
||||
do {
|
||||
try {
|
||||
FileList files = request.execute();
|
||||
FileList files = request.execute();
|
||||
|
||||
for (File file : files.getItems()) {
|
||||
idToFileMap.put(file.getId(), file);
|
||||
}
|
||||
request.setPageToken(files.getNextPageToken());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
request.setPageToken(null);
|
||||
for (File file : files.getItems()) {
|
||||
idToFileMap.put(file.getId(), file);
|
||||
}
|
||||
request.setPageToken(files.getNextPageToken());
|
||||
} while (request.getPageToken() != null && request.getPageToken().length() > 0);
|
||||
return idToFileMap;
|
||||
}
|
||||
@@ -323,38 +347,35 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
* @param changes a map of drive id to file for the changes
|
||||
* @return an updated largest change id
|
||||
*/
|
||||
private long getDriveChanges(String folderId, long changeId, Map<String, File> changes) {
|
||||
try {
|
||||
Changes.List request = drive.changes().list().setStartChangeId(changeId + 1);
|
||||
do {
|
||||
ChangeList changeList = request.execute();
|
||||
long newId = changeList.getLargestChangeId().longValue();
|
||||
private long getDriveChanges(String folderId, long changeId, Map<String, File> changes)
|
||||
throws IOException {
|
||||
Changes.List request = drive.changes().list().setStartChangeId(changeId + 1);
|
||||
do {
|
||||
ChangeList changeList = request.execute();
|
||||
long newId = changeList.getLargestChangeId().longValue();
|
||||
|
||||
for (Change change : changeList.getItems()) {
|
||||
if (change.getDeleted()) {
|
||||
changes.put(change.getFileId(), null);
|
||||
} else {
|
||||
File file = change.getFile();
|
||||
if (SyncUtils.isInFolder(file, folderId)) {
|
||||
if (file.getLabels().getTrashed()) {
|
||||
changes.put(change.getFileId(), null);
|
||||
} else {
|
||||
changes.put(change.getFileId(), file);
|
||||
}
|
||||
} else if (SyncUtils.isSharedWithMe(file)) {
|
||||
for (Change change : changeList.getItems()) {
|
||||
if (change.getDeleted()) {
|
||||
changes.put(change.getFileId(), null);
|
||||
} else {
|
||||
File file = change.getFile();
|
||||
if (SyncUtils.isInFolder(file, folderId)) {
|
||||
if (file.getLabels().getTrashed()) {
|
||||
changes.put(change.getFileId(), null);
|
||||
} else {
|
||||
changes.put(change.getFileId(), file);
|
||||
}
|
||||
} else if (SyncUtils.isSharedWithMe(file)) {
|
||||
changes.put(change.getFileId(), file);
|
||||
}
|
||||
}
|
||||
if (newId > changeId) {
|
||||
changeId = newId;
|
||||
}
|
||||
request.setPageToken(changeList.getNextPageToken());
|
||||
} while (request.getPageToken() != null && request.getPageToken().length() > 0);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
}
|
||||
Log.d(TAG, "Got drive changes: " + changes.size() + " - " + changeId);
|
||||
}
|
||||
if (newId > changeId) {
|
||||
changeId = newId;
|
||||
}
|
||||
request.setPageToken(changeList.getNextPageToken());
|
||||
} while (request.getPageToken() != null && request.getPageToken().length() > 0);
|
||||
Log.d(TAG, "Got drive changes: " + changes.size() + " " + changeId);
|
||||
return changeId;
|
||||
}
|
||||
|
||||
@@ -364,44 +385,77 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
* @param track the track
|
||||
* @param driveFile the drive file
|
||||
*/
|
||||
private void mergeFiles(Track track, File driveFile) throws Exception {
|
||||
private void merge(Track track, File driveFile) throws IOException {
|
||||
long modifiedTime = track.getModifiedTime();
|
||||
long driveModifiedTime = driveFile.getModifiedDate().getValue();
|
||||
if (modifiedTime > driveModifiedTime) {
|
||||
Log.d(TAG, "Updating track change " + track.getName());
|
||||
if (!SyncUtils.updateDriveFile(context, myTracksProviderUtils, drive, driveFile, track)) {
|
||||
|
||||
// TODO: Should inform the user if cannot update the file
|
||||
Log.d(TAG, "Updating track change for track " + track.getName() + " and drive file "
|
||||
+ driveFile.getOriginalFilename());
|
||||
if (!SyncUtils.updateDriveFile(
|
||||
drive, driveFile, context, myTracksProviderUtils, track, true)) {
|
||||
Log.e(TAG, "Unable to update drive file");
|
||||
track.setModifiedTime(driveModifiedTime);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
}
|
||||
} else if (modifiedTime < driveModifiedTime) {
|
||||
Log.d(TAG, "Updating drive change " + track.getName());
|
||||
InputStream inputStream = downloadDriveFile(driveFile);
|
||||
if (inputStream == null) {
|
||||
|
||||
// TODO: Should retry if cannot download
|
||||
Log.e(TAG, "Unable to update drive change. Input stream is null for " + track.getName());
|
||||
track.setModifiedTime(driveModifiedTime);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
} else {
|
||||
Log.d(TAG, "Updating drive change for track " + track.getName() + " and drive file "
|
||||
+ driveFile.getOriginalFilename());
|
||||
InputStream inputStream = downloadDriveFile(driveFile, true);
|
||||
if (inputStream != null) {
|
||||
KmlImporter kmlImporter = new KmlImporter(context, track.getId());
|
||||
long[] tracksIds = kmlImporter.importFile(inputStream);
|
||||
if (tracksIds.length == 1) {
|
||||
Track newTrack = myTracksProviderUtils.getTrack(tracksIds[0]);
|
||||
newTrack.setDriveId(driveFile.getId());
|
||||
newTrack.setModifiedTime(driveModifiedTime);
|
||||
newTrack.setSharedWithMe(driveFile.getSharedWithMeDate() != null);
|
||||
myTracksProviderUtils.updateTrack(newTrack);
|
||||
} else {
|
||||
|
||||
/*
|
||||
* TODO: Should revert the track back to the original.
|
||||
*/
|
||||
Log.e(
|
||||
TAG, "Unable to update drive change. Imported size is not 1 for " + track.getName());
|
||||
try {
|
||||
long[] trackIds = kmlImporter.importFile(inputStream);
|
||||
if (trackIds.length == 1) {
|
||||
Track newTrack = myTracksProviderUtils.getTrack(trackIds[0]);
|
||||
newTrack.setDriveId(driveFile.getId());
|
||||
newTrack.setModifiedTime(driveModifiedTime);
|
||||
newTrack.setSharedWithMe(driveFile.getSharedWithMeDate() != null);
|
||||
myTracksProviderUtils.updateTrack(newTrack);
|
||||
return;
|
||||
} else {
|
||||
Log.e(TAG, "Unable to merge, imported size is not 1");
|
||||
}
|
||||
} catch (SAXException e) {
|
||||
Log.e(TAG, "Unable to merge", e);
|
||||
} catch (ParserConfigurationException e) {
|
||||
Log.e(TAG, "Unable to merge", e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to merge", e);
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "Unable to update drive change");
|
||||
track.setModifiedTime(driveModifiedTime);
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a drive file.
|
||||
*
|
||||
* @param driveId the drive id
|
||||
* @param folderId the folder id
|
||||
* @param canRetry true if can retry the request
|
||||
*/
|
||||
private void deleteDriveFile(String driveId, String folderId, boolean canRetry)
|
||||
throws UserRecoverableAuthIOException {
|
||||
try {
|
||||
File driveFile = drive.files().get(driveId).execute();
|
||||
if (SyncUtils.isInFolder(driveFile, folderId)) {
|
||||
if (!driveFile.getLabels().getTrashed()) {
|
||||
drive.files().trash(driveId).execute();
|
||||
}
|
||||
// if trashed, ignore
|
||||
} else if (SyncUtils.isSharedWithMe(driveFile)) {
|
||||
drive.files().delete(driveId).execute();
|
||||
}
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (canRetry) {
|
||||
deleteDriveFile(driveId, folderId, false);
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "Unable to delete Drive file for " + driveId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,24 +464,26 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
*
|
||||
* @param driveFile the drive file
|
||||
*/
|
||||
private InputStream downloadDriveFile(File driveFile) {
|
||||
if (driveFile.getDownloadUrl() != null && driveFile.getDownloadUrl().length() > 0) {
|
||||
try {
|
||||
HttpResponse httpResponse = drive.getRequestFactory()
|
||||
.buildGetRequest(new GenericUrl(driveFile.getDownloadUrl())).execute();
|
||||
if (httpResponse != null) {
|
||||
return httpResponse.getContent();
|
||||
} else {
|
||||
Log.e(TAG, "http response is null");
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
private InputStream downloadDriveFile(File driveFile, boolean canRetry) throws IOException {
|
||||
if (driveFile.getDownloadUrl() == null || driveFile.getDownloadUrl().length() == 0) {
|
||||
Log.d(TAG, "Drive file download url doesn't exist: " + driveFile.getTitle());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
HttpResponse httpResponse = drive.getRequestFactory()
|
||||
.buildGetRequest(new GenericUrl(driveFile.getDownloadUrl())).execute();
|
||||
if (httpResponse == null) {
|
||||
Log.e(TAG, "http response is null");
|
||||
return null;
|
||||
}
|
||||
return httpResponse.getContent();
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (canRetry) {
|
||||
return downloadDriveFile(driveFile, false);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class SyncUtils {
|
||||
|
||||
// Get My Tracks folder KML files
|
||||
public static final String MY_TRACKS_FOLDER_FILES_QUERY = "'%s' in parents and mimeType = '"
|
||||
+ KML_MIME_TYPE + "' and trashed = false and not sharedWithMe";
|
||||
+ KML_MIME_TYPE + "' and trashed = false";
|
||||
|
||||
// Get shared with me KML files
|
||||
public static final String SHARED_WITH_ME_FILES_QUERY = "sharedWithMe and mimeType = '"
|
||||
@@ -107,6 +107,21 @@ public class SyncUtils {
|
||||
|
||||
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}.
|
||||
@@ -130,7 +145,7 @@ public class SyncUtils {
|
||||
});
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
activity.startActivityForResult(e.getIntent(), DRIVE_PERMISSION_REQUEST_CODE);
|
||||
} catch (GoogleAuthException e) {
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -155,24 +170,26 @@ public class SyncUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the drive credential. Needs to be run in a background thread.
|
||||
* Sends a notification to request permission.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param intent the intent
|
||||
*/
|
||||
public static GoogleAccountCredential getCredential(Context context, String accountName) {
|
||||
try {
|
||||
return getGoogleAccountCredential(context, accountName);
|
||||
} catch (UserRecoverableAuthException e) {
|
||||
sendNotification(context, accountName, e.getIntent());
|
||||
} catch (GoogleAuthException e) {
|
||||
Log.e(TAG, "GoogleAuthException", e);
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
sendNotification(context, accountName, e.getIntent());
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
}
|
||||
return null;
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,17 +327,19 @@ public class SyncUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Drive file.
|
||||
* Inserts a Drive file.
|
||||
*
|
||||
* @param context the context
|
||||
* @param myTracksProviderUtils the myTracksProviderUtils
|
||||
* @param drive the drive
|
||||
* @param folderId the folder id
|
||||
* @param context the context
|
||||
* @param myTracksProviderUtils the myTracksProviderUtils
|
||||
* @param track the track
|
||||
* @param canRetry true if can retry
|
||||
* @return the added drive file id or null.
|
||||
*/
|
||||
public static String addDriveFile(Context context, MyTracksProviderUtils myTracksProviderUtils,
|
||||
Drive drive, String folderId, Track track) throws IOException {
|
||||
public static String insertDriveFile(Drive drive, String folderId, Context context,
|
||||
MyTracksProviderUtils myTracksProviderUtils, Track track, boolean canRetry)
|
||||
throws IOException {
|
||||
java.io.File file = getFile(context, myTracksProviderUtils, track);
|
||||
if (file == null) {
|
||||
Log.e(TAG, "Unable to add Drive file. File is null for track " + track.getName());
|
||||
@@ -329,21 +348,7 @@ public class SyncUtils {
|
||||
|
||||
try {
|
||||
Log.d(TAG, "Add Drive file for track " + track.getName());
|
||||
FileContent fileContent = new FileContent(KML_MIME_TYPE, file);
|
||||
|
||||
// file's parent
|
||||
ParentReference parentReference = new ParentReference();
|
||||
parentReference.setId(folderId);
|
||||
ArrayList<ParentReference> parents = new ArrayList<ParentReference>();
|
||||
parents.add(parentReference);
|
||||
|
||||
// file's metadata
|
||||
File newMetaData = new File();
|
||||
newMetaData.setTitle(track.getName() + "." + TrackFileFormat.KML.getExtension());
|
||||
newMetaData.setMimeType(KML_MIME_TYPE);
|
||||
newMetaData.setParents(parents);
|
||||
|
||||
File uploadedFile = drive.files().insert(newMetaData, fileContent).execute();
|
||||
File uploadedFile = insertDriveFile(drive, folderId, track.getName(), file, canRetry);
|
||||
if (uploadedFile == null) {
|
||||
Log.e(TAG, "Unable to add Drive file. Uploaded file is null for track " + track.getName());
|
||||
return null;
|
||||
@@ -360,16 +365,55 @@ public class SyncUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Drive file.
|
||||
* Inserts a Drive file.
|
||||
*
|
||||
* @param drive the drive
|
||||
* @param folderId the folder id
|
||||
* @param name the track name
|
||||
* @param file the track file
|
||||
* @param canRetry true if can retry
|
||||
*/
|
||||
private static File insertDriveFile(
|
||||
Drive drive, String folderId, String name, java.io.File file, boolean canRetry)
|
||||
throws IOException {
|
||||
try {
|
||||
FileContent fileContent = new FileContent(KML_MIME_TYPE, file);
|
||||
|
||||
// file's parent
|
||||
ParentReference parentReference = new ParentReference();
|
||||
parentReference.setId(folderId);
|
||||
ArrayList<ParentReference> parents = new ArrayList<ParentReference>();
|
||||
parents.add(parentReference);
|
||||
|
||||
// file's metadata
|
||||
File newMetaData = new File();
|
||||
newMetaData.setTitle(name + "." + TrackFileFormat.KML.getExtension());
|
||||
newMetaData.setMimeType(KML_MIME_TYPE);
|
||||
newMetaData.setParents(parents);
|
||||
|
||||
return drive.files().insert(newMetaData, fileContent).execute();
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (canRetry) {
|
||||
return insertDriveFile(drive, folderId, name, file, false);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Drive file. Returns true if successful.
|
||||
*
|
||||
* @param context the context
|
||||
* @param myTracksProviderUtils the myTracksProviderUtils
|
||||
* @param drive the drive
|
||||
* @param driveFile the drive file
|
||||
* @param context the context
|
||||
* @param myTracksProviderUtils the myTracksProviderUtils
|
||||
* @param track the track
|
||||
* @param canRetry true if can retry
|
||||
*/
|
||||
public static boolean updateDriveFile(Context context,
|
||||
MyTracksProviderUtils myTracksProviderUtils, Drive drive, File driveFile, Track track)
|
||||
public static boolean updateDriveFile(Drive drive, File driveFile, Context context,
|
||||
MyTracksProviderUtils myTracksProviderUtils, Track track, boolean canRetry)
|
||||
throws IOException {
|
||||
Log.d(TAG, "Update drive file for track " + track.getName());
|
||||
java.io.File file = SyncUtils.getFile(context, myTracksProviderUtils, track);
|
||||
@@ -379,18 +423,13 @@ public class SyncUtils {
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
FileContent fileContent = new FileContent(KML_MIME_TYPE, file);
|
||||
|
||||
driveFile.setTitle(track.getName() + "." + TrackFileFormat.KML.getExtension());
|
||||
File updatedFile = drive.files()
|
||||
.update(driveFile.getId(), driveFile, fileContent).execute();
|
||||
File updatedFile = updateDriveFile(drive, driveFile, track.getName(), file, canRetry);
|
||||
if (updatedFile == null) {
|
||||
Log.e(TAG,
|
||||
"Unable to update drive file. Updated file is null for track " + track.getName());
|
||||
return false;
|
||||
}
|
||||
long newModifiedTime = updatedFile.getModifiedDate().getValue();
|
||||
track.setModifiedTime(newModifiedTime);
|
||||
track.setModifiedTime(updatedFile.getModifiedDate().getValue());
|
||||
myTracksProviderUtils.updateTrack(track);
|
||||
return true;
|
||||
} finally {
|
||||
@@ -400,43 +439,31 @@ public class SyncUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the google account credential.
|
||||
* Updates a Drive file.
|
||||
*
|
||||
* @param context the context
|
||||
* @param accountName the account name
|
||||
* @param drive the drive
|
||||
* @param driveFile the drive file
|
||||
* @param name the track name
|
||||
* @param file the track file
|
||||
* @param canRetry true if can retry
|
||||
*/
|
||||
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;
|
||||
private static File updateDriveFile(
|
||||
Drive drive, File driveFile, String name, java.io.File file, boolean canRetry)
|
||||
throws IOException {
|
||||
try {
|
||||
FileContent fileContent = new FileContent(KML_MIME_TYPE, file);
|
||||
driveFile.setTitle(name + "." + TrackFileFormat.KML.getExtension());
|
||||
return drive.files().update(driveFile.getId(), driveFile, fileContent).execute();
|
||||
} catch (UserRecoverableAuthIOException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
if (canRetry) {
|
||||
return updateDriveFile(drive, driveFile, name, file, false);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a file from a track.
|
||||
*
|
||||
|
||||
+2
-8
@@ -24,8 +24,6 @@ import com.google.api.services.drive.Drive;
|
||||
import android.app.Instrumentation;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests the situation when user use multiple account in MyTracks.
|
||||
*
|
||||
@@ -63,10 +61,8 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
* <li>8. Enable sync with account2.</li>
|
||||
* <li>9. Check</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void testSyncTracksWithMultiAccounts() throws IOException {
|
||||
public void testSyncTracksWithMultiAccounts() throws Exception {
|
||||
if (!SyncTestUtils.runSyncTest) {
|
||||
return;
|
||||
}
|
||||
@@ -108,10 +104,8 @@ public class MultiAccountsSyncTest extends ActivityInstrumentationTestCase2<Trac
|
||||
* <li>5. Sync with account 1 (account 1 should have 1 file, account2 should
|
||||
* also have 1 file).</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void testDeleteTracksWithMultiAccounts() throws IOException {
|
||||
public void testDeleteTracksWithMultiAccounts() throws Exception {
|
||||
if (!SyncTestUtils.runSyncTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
+7
-6
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.endtoendtest.EndToEndTestUtils;
|
||||
import com.google.android.apps.mytracks.endtoendtest.GoogleUtils;
|
||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.gms.auth.GoogleAuthException;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
|
||||
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||
@@ -61,10 +62,9 @@ public class SyncTestUtils {
|
||||
* @param instrumentation the instrumentation is used for test
|
||||
* @param trackListActivity the startup activity
|
||||
* @return a Google Drive object
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Drive setUpForSyncTest(Instrumentation instrumentation,
|
||||
TrackListActivity trackListActivity) throws IOException {
|
||||
TrackListActivity trackListActivity) throws IOException, GoogleAuthException {
|
||||
if (runSyncTest || !isCheckedRunSyncTest) {
|
||||
EndToEndTestUtils.setupForAllTest(instrumentation, trackListActivity);
|
||||
}
|
||||
@@ -116,10 +116,11 @@ public class SyncTestUtils {
|
||||
* @param context the context of application
|
||||
* @return a Google Drive object
|
||||
*/
|
||||
public static Drive getGoogleDrive(Context context) {
|
||||
String googleAccount = PreferencesUtils.getString(context, R.string.google_account_key,
|
||||
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
|
||||
GoogleAccountCredential credential = SyncUtils.getCredential(context, googleAccount);
|
||||
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);
|
||||
return SyncUtils.getDriveService(credential);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user