diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java index e4fa9aad1..c0a798de2 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncAdapter.java @@ -393,7 +393,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { long driveModifiedTime = driveFile.getModifiedDate().getValue(); if (modifiedTime > driveModifiedTime) { Log.d(TAG, "Updating track change for track " + track.getName() + " and drive file " - + driveFile.getOriginalFilename()); + + driveFile.getTitle()); if (!SyncUtils.updateDriveFile( drive, driveFile, context, myTracksProviderUtils, track, true)) { Log.e(TAG, "Unable to update drive file"); @@ -402,36 +402,64 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { } } else if (modifiedTime < driveModifiedTime) { 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()); - 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); - } + + driveFile.getTitle()); + if (!updateTrack(track, driveFile)) { + Log.e(TAG, "Unable to update drive change"); + track.setModifiedTime(driveModifiedTime); + myTracksProviderUtils.updateTrack(track); } - Log.e(TAG, "Unable to update drive change"); - track.setModifiedTime(driveModifiedTime); - myTracksProviderUtils.updateTrack(track); } } + /** + * Updates track based on drive file changes. Returns true if successful. + * + * @param track the track + * @param driveFile the drive file + */ + private boolean updateTrack(Track track, File driveFile) throws IOException { + java.io.File file = null; + try { + file = SyncUtils.getFile(context, myTracksProviderUtils, track); + String digest = SyncUtils.md5(file); + if (digest != null && digest.equals(driveFile.getMd5Checksum())) { + track.setModifiedTime(driveFile.getModifiedDate().getValue()); + myTracksProviderUtils.updateTrack(track); + return true; + } + } finally { + if (file != null) { + file.delete(); + } + } + InputStream inputStream = downloadDriveFile(driveFile, true); + if (inputStream == null) { + Log.e(TAG, "Unable to update track. Input stream is null for track " + track.getName()); + return false; + } + KmlImporter kmlImporter = new KmlImporter(context, track.getId()); + try { + long[] trackIds = kmlImporter.importFile(inputStream); + if (trackIds.length == 1) { + Track newTrack = myTracksProviderUtils.getTrack(trackIds[0]); + newTrack.setDriveId(driveFile.getId()); + newTrack.setModifiedTime(driveFile.getModifiedByMeDate().getValue()); + newTrack.setSharedWithMe(driveFile.getSharedWithMeDate() != null); + myTracksProviderUtils.updateTrack(newTrack); + return true; + } 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); + } + return false; + } + /** * Deletes a drive file. * diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncUtils.java b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncUtils.java index 85862f1fc..de944a12c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sync/SyncUtils.java @@ -53,9 +53,13 @@ import android.os.Bundle; import android.support.v4.app.NotificationCompat; import android.util.Log; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Locale; @@ -422,14 +426,28 @@ public class SyncUtils { return false; } else { try { - File updatedFile = updateDriveFile(drive, driveFile, track.getName(), file, canRetry); + String title = track.getName() + "." + TrackFileFormat.KML.getExtension(); + File updatedFile; + String digest = md5(file); + if (digest != null && digest.equals(driveFile.getMd5Checksum())) { + if (title.equals(driveFile.getTitle())) { + updatedFile = driveFile; + } else { + updatedFile = updateDriveFile(drive, driveFile, title, null, canRetry); + } + } else { + updatedFile = updateDriveFile(drive, driveFile, title, file, canRetry); + } if (updatedFile == null) { Log.e(TAG, "Unable to update drive file. Updated file is null for track " + track.getName()); return false; } - track.setModifiedTime(updatedFile.getModifiedDate().getValue()); - myTracksProviderUtils.updateTrack(track); + long modifiedTime = updatedFile.getModifiedDate().getValue(); + if (track.getModifiedTime() != modifiedTime) { + track.setModifiedTime(modifiedTime); + myTracksProviderUtils.updateTrack(track); + } return true; } finally { file.delete(); @@ -442,22 +460,26 @@ public class SyncUtils { * * @param drive the drive * @param driveFile the drive file - * @param name the track name + * @param driveTitle the drive title * @param file the track file * @param canRetry true if can retry */ private static File updateDriveFile( - Drive drive, File driveFile, String name, java.io.File file, boolean canRetry) + Drive drive, File driveFile, String driveTitle, 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(); + driveFile.setTitle(driveTitle); + if (file != null) { + return drive.files() + .update(driveFile.getId(), driveFile, new FileContent(KML_MIME_TYPE, file)).execute(); + } else { + return drive.files().update(driveFile.getId(), driveFile).execute(); + } } catch (UserRecoverableAuthIOException e) { throw e; } catch (IOException e) { if (canRetry) { - return updateDriveFile(drive, driveFile, name, file, false); + return updateDriveFile(drive, driveFile, driveTitle, file, false); } throw e; } @@ -470,7 +492,7 @@ public class SyncUtils { * @param myTracksProviderUtils the myTracksProviderUtils * @param track the track */ - private static java.io.File getFile( + public static java.io.File getFile( Context context, MyTracksProviderUtils myTracksProviderUtils, Track track) throws FileNotFoundException { TrackFileFormat trackFileFormat = TrackFileFormat.KML; @@ -494,4 +516,52 @@ public class SyncUtils { Log.d(TAG, "Unable to get file for track " + track.getName()); return null; } + + /** + * Gets the md5 digest for the file. + * + * @param file the file + */ + public static String md5(java.io.File file) { + if (file == null) { + return null; + } + InputStream in = null; + byte[] digest; + try { + in = new FileInputStream(file); + MessageDigest digester = MessageDigest.getInstance("MD5"); + byte[] bytes = new byte[8192]; + int byteCount; + while ((byteCount = in.read(bytes)) > 0) { + digester.update(bytes, 0, byteCount); + } + digest = digester.digest(); + + StringBuilder builder = new StringBuilder(digest.length * 2); + for (byte b : digest) { + if ((b & 0xFF) < 0x10) { + builder.append("0"); + } + builder.append(Integer.toHexString(b & 0xFF)); + } + return builder.toString(); + + } catch (IOException e) { + Log.e(TAG, "IOException", e); + return null; + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "NoSuchAlgorithmException", e); + return null; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + Log.e(TAG, "Unable to close inputstream", e); + return null; + } + } + } + } }