diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml index 6de0c7dd0..4259acde6 100644 --- a/MyTracks/res/values/keys.xml +++ b/MyTracks/res/values/keys.xml @@ -63,6 +63,7 @@ limitations under the License. autoResumeTrackCurrentRetry bluetoothSensor driveDeletedList + driveEditedList driveLargestChangeId driveSync googleAccount diff --git a/MyTracks/src/com/google/android/apps/mytracks/DeleteAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/DeleteAsyncTask.java index 99ba963c7..02d3b617c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/DeleteAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/DeleteAsyncTask.java @@ -17,6 +17,7 @@ package com.google.android.apps.mytracks; */ import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.io.sync.SyncUtils; import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.maps.mytracks.R; @@ -97,7 +98,19 @@ public class DeleteAsyncTask extends AsyncTask { if (isCancelled()) { return false; } + Track track = myTracksProviderUtils.getTrack(id); myTracksProviderUtils.deleteTrack(id); + if (track != null) { + String driveId = track.getDriveId(); + if (driveId != null && !driveId.equals("")) { + boolean driveSync = PreferencesUtils.getBoolean( + context, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT); + if (driveSync) { + PreferencesUtils.addToList(context, R.string.drive_deleted_list_key, + PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT, driveId); + } + } + } } return true; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java b/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java index 2846df0f8..222702515 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MarkerEditActivity.java @@ -17,10 +17,12 @@ package com.google.android.apps.mytracks; import com.google.android.apps.mytracks.content.MyTracksProviderUtils; +import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.Waypoint.WaypointType; import com.google.android.apps.mytracks.content.WaypointCreationRequest; import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection; +import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils; import com.google.android.maps.mytracks.R; @@ -123,6 +125,17 @@ public class MarkerEditActivity extends AbstractMyTracksActivity { } else { saveMarker(); } + boolean driveSync = PreferencesUtils.getBoolean( + MarkerEditActivity.this, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT); + if (driveSync) { + MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get( + MarkerEditActivity.this); + Track track = myTracksProviderUtils.getTrack(newMarker ? trackId : waypoint.getTrackId()); + track.setModifiedTime(System.currentTimeMillis()); + myTracksProviderUtils.updateTrack(track); + PreferencesUtils.addToList(MarkerEditActivity.this, R.string.drive_edited_list_key, + PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT, String.valueOf(track.getId())); + } finish(); } }); diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java index 2b130009c..1cdc1e4c7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java @@ -79,7 +79,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity implements public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_MARKER_ID = "marker_id"; - private static final String TAG = TrackDetailActivity.class.getSimpleName(); private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key"; private static final String PHOTO_URI_KEY = "photo_uri_key"; private static final String HAS_PHOTO_KEY = "has_photo_key"; diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java index 0a479b78e..f938edc68 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/TrackEditActivity.java @@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragme import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection; import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.apps.mytracks.util.CalorieUtils; +import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.apps.mytracks.util.TrackIconUtils; import com.google.android.apps.mytracks.util.TrackNameUtils; import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils; @@ -199,6 +200,12 @@ public class TrackEditActivity extends AbstractMyTracksActivity track.setDescription(description.getText().toString()); track.setModifiedTime(System.currentTimeMillis()); myTracksProviderUtils.updateTrack(track); + boolean driveSync = PreferencesUtils.getBoolean( + TrackEditActivity.this, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT); + if (driveSync) { + PreferencesUtils.addToList(TrackEditActivity.this, R.string.drive_edited_list_key, + PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT, String.valueOf(track.getId())); + } finish(); } }); diff --git a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java index 30a6c00a9..ba815b0e7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java +++ b/MyTracks/src/com/google/android/apps/mytracks/content/MyTracksProvider.java @@ -16,7 +16,6 @@ package com.google.android.apps.mytracks.content; -import com.google.android.apps.mytracks.io.sync.SyncUtils; import com.google.android.apps.mytracks.util.FileUtils; import com.google.android.apps.mytracks.util.PreferencesUtils; import com.google.android.maps.mytracks.R; @@ -42,7 +41,6 @@ import android.util.Log; import java.io.File; import java.io.FileNotFoundException; -import java.util.ArrayList; /** * A {@link ContentProvider} that handles access to track points, tracks, and @@ -218,18 +216,6 @@ public class MyTracksProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URL " + url); } - boolean driveSync = false; - String driveIds = ""; - if (table.equals(TracksColumns.TABLE_NAME)) { - driveSync = PreferencesUtils.getBoolean( - getContext(), R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT); - if (driveSync) { - driveIds = where != null ? getDriveIds(null, where, selectionArgs) - : getDriveIds( - new String[] { TracksColumns.DRIVEID }, SyncUtils.DRIVE_ID_TRACKS_QUERY, null); - } - } - Log.w(MyTracksProvider.TAG, "Deleting table " + table); int count; try { @@ -240,17 +226,6 @@ public class MyTracksProvider extends ContentProvider { db.endTransaction(); } getContext().getContentResolver().notifyChange(url, null, false); - - if (driveSync && table.equals(TracksColumns.TABLE_NAME)) { - String driveDeletedList = PreferencesUtils.getString(getContext(), - R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT); - if (driveDeletedList.equals(PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT)) { - driveDeletedList = driveIds; - } else { - driveDeletedList += ";" + driveIds; - } - PreferencesUtils.setString(getContext(), R.string.drive_deleted_list_key, driveDeletedList); - } if (shouldVacuum) { // If a potentially large amount of data was deleted, reclaim its space. @@ -536,33 +511,4 @@ public class MyTracksProvider extends ContentProvider { } throw new SQLException("Failed to insert a waypoint " + url); } - - /** - * Gets a list of dirve ids. - * - * @param projection the projection - * @param where where - * @param selectionArgs selection args - */ - private String getDriveIds(String[] projection, String where, String[] selectionArgs) { - ArrayList driveIds = new ArrayList(); - Cursor cursor = null; - try { - cursor = query(TracksColumns.CONTENT_URI, projection, where, selectionArgs, null); - if (cursor != null && cursor.moveToFirst()) { - int index = cursor.getColumnIndex(TracksColumns.DRIVEID); - do { - String driveId = cursor.getString(index); - if (driveId != null && !driveId.equals("")) { - driveIds.add(driveId); - } - } while (cursor.moveToNext()); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return TextUtils.join(";", driveIds); - } } \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java index e030666ff..5ae8e2aba 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java @@ -34,6 +34,7 @@ import com.google.android.apps.mytracks.util.UnitConversions; import com.google.android.maps.mytracks.R; import com.google.common.annotations.VisibleForTesting; +import android.content.Context; import android.location.Location; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -188,7 +189,7 @@ public class ChartFragment extends Fragment implements TrackDataListener { public void onSampledInTrackPoint(Location location) { if (isResumed()) { double[] data = new double[ChartView.NUM_SERIES + 1]; - fillDataPoint(location, data); + fillDataPoint(location, data, getActivity()); pendingPoints.add(data); } } @@ -196,14 +197,14 @@ public class ChartFragment extends Fragment implements TrackDataListener { @Override public void onSampledOutTrackPoint(Location location) { if (isResumed()) { - fillDataPoint(location, null); + fillDataPoint(location, null, getActivity()); } } @Override public void onSegmentSplit(Location location) { if (isResumed()) { - fillDataPoint(location, null); + fillDataPoint(location, null, getActivity()); } } @@ -439,9 +440,10 @@ public class ChartFragment extends Fragment implements TrackDataListener { * * @param location the location * @param data the data point to fill in, can be null + * @param context the context */ @VisibleForTesting - void fillDataPoint(Location location, double data[]) { + void fillDataPoint(Location location, double data[], Context context) { double timeOrDistance = Double.NaN; double elevation = Double.NaN; double speed = Double.NaN; @@ -451,7 +453,7 @@ public class ChartFragment extends Fragment implements TrackDataListener { double power = Double.NaN; if (tripStatisticsUpdater != null) { - tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, getActivity()); + tripStatisticsUpdater.addLocation(location, recordingDistanceInterval, context); TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics(); if (chartByDistance) { double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM; diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java b/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java index 5e88d1309..ee8e03420 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/backup/PreferenceBackupHelper.java @@ -19,6 +19,7 @@ import com.google.android.apps.mytracks.content.ContentTypeIds; import com.google.android.apps.mytracks.util.ApiAdapterFactory; import com.google.android.maps.mytracks.R; +import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -52,6 +53,7 @@ public class PreferenceBackupHelper { doNotBackup.add(context.getString(R.string.auto_resume_track_current_retry_key)); doNotBackup.add(context.getString(R.string.bluetooth_sensor_key)); doNotBackup.add(context.getString(R.string.drive_deleted_list_key)); + doNotBackup.add(context.getString(R.string.drive_edited_list_key)); doNotBackup.add(context.getString(R.string.drive_largest_change_id_key)); doNotBackup.add(context.getString(R.string.drive_sync_key)); doNotBackup.add(context.getString(R.string.google_account_key)); @@ -117,6 +119,7 @@ public class PreferenceBackupHelper { * @param preferences the shared preferences to edit * @throws IOException if there are any errors while reading */ + @SuppressLint("CommitPrefEdits") public void importPreferences(DataInputStream reader, SharedPreferences preferences) throws IOException { Editor editor = preferences.edit(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/drive/SendDriveAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/drive/SendDriveAsyncTask.java index f96709b2c..e1b39b95d 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/drive/SendDriveAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/drive/SendDriveAsyncTask.java @@ -90,14 +90,16 @@ 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( - drive, driveFile, context, myTracksProviderUtils, track, false)) { + if (driveFile != null && updateDriveFile(drive, driveFile, folderId, track)) { addPermission(drive, driveFile); return true; } + + // clear sync state SyncUtils.updateTrack(myTracksProviderUtils, track, null); } + // insert new drive file File file = SyncUtils.insertDriveFile( drive, folderId, context, myTracksProviderUtils, track, false); if (file == null) { @@ -119,6 +121,29 @@ public class SendDriveAsyncTask extends AbstractSendAsyncTask { return retryTask(); } } + + /** + * Updates a drive file using info from a track. Returns true if successful. + * + * @param drive the drive + * @param driveFile the drive file + * @param folderId the folder id of the drive file + * @param track the track + */ + private boolean updateDriveFile(Drive drive, File driveFile, String folderId, Track track) + throws IOException { + if (!SyncUtils.isInMyTracksAndValid(driveFile, folderId)) { + return false; + } + long driveModifiedTime = driveFile.getModifiedDate().getValue(); + long modifiedTime = track.getModifiedTime(); + + if (driveModifiedTime != modifiedTime) { + return SyncUtils.updateDriveFile( + drive, driveFile, context, myTracksProviderUtils, track, true); + } + return true; + } @Override protected void invalidateToken() {} 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 14156ca4b..ae2fdaf18 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 @@ -57,6 +57,7 @@ import java.io.InputStream; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -77,7 +78,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { private final MyTracksProviderUtils myTracksProviderUtils; private Drive drive; private String driveAccountName; // the account name associated with the drive - + private String folderId; + public SyncAdapter(Context context) { super(context, true); this.context = context; @@ -118,22 +120,14 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { driveAccountName = account.name; } - File folder = SyncUtils.getMyTracksFolder(context, drive); - if (folder == null) { - return; - } - String folderId = folder.getId(); - if (folderId == null) { - return; - } long largestChangeId = PreferencesUtils.getLong( context, R.string.drive_largest_change_id_key); if (largestChangeId == PreferencesUtils.DRIVE_LARGEST_CHANGE_ID_DEFAULT) { - performInitialSync(folderId); + performInitialSync(); } else { - performIncrementalSync(folderId, largestChangeId); + performIncrementalSync(largestChangeId); } - insertNewDriveFiles(folderId); + insertNewDriveFiles(); } catch (UserRecoverableAuthException e) { SendToGoogleUtils.sendNotification( context, account.name, e.getIntent(), SendToGoogleUtils.DRIVE_NOTIFICATION_ID); @@ -148,23 +142,38 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { } /** - * Performs initial sync. - * - * @param folderId the folder id + * Gets the folder id.. */ - private void performInitialSync(String folderId) throws IOException { + private String getFolderId() throws IOException { + if (folderId == null) { + File folder = SyncUtils.getMyTracksFolder(context, drive); + if (folder == null) { + throw new IOException("folder is null"); + } + folderId = folder.getId(); + if (folderId == null) { + throw new IOException("folder id is null"); + } + } + return folderId; + } + + /** + * Performs initial sync. + */ + private void performInitialSync() 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/KMZ files in the "My Drive:/My Tracks" folder - Files.List myTracksFolderRequest = drive.files() - .list().setQ(String.format(Locale.US, SyncUtils.MY_TRACKS_FOLDER_FILES_QUERY, folderId)); + Files.List myTracksFolderRequest = drive.files().list() + .setQ(String.format(Locale.US, SyncUtils.MY_TRACKS_FOLDER_FILES_QUERY, getFolderId())); Map myTracksFolderMap = getFiles(myTracksFolderRequest, true); // Handle tracks that are already uploaded to Google Drive - Set syncedDriveIds = updateSyncedTracks(folderId); + Set syncedDriveIds = updateSyncedTracks(); for (String driveId : syncedDriveIds) { myTracksFolderMap.remove(driveId); } @@ -204,10 +213,9 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Updates synced tracks. * - * @param folderId the folder id * @return drive ids of the synced tracks */ - private Set updateSyncedTracks(String folderId) throws IOException { + private Set updateSyncedTracks() throws IOException { Set result = new HashSet(); Cursor cursor = null; try { @@ -219,7 +227,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { if (driveId != null && !driveId.equals("")) { if (!track.isSharedWithMe()) { File driveFile = drive.files().get(driveId).execute(); - if (SyncUtils.isValid(driveFile, folderId)) { + if (SyncUtils.isInMyTracksAndValid(driveFile, getFolderId())) { merge(track, driveFile); result.add(driveId); } else { @@ -245,79 +253,107 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Performs incremental sync. * - * @param folderId the folder id * @param largestChangeId the largest change id */ - private void performIncrementalSync(String folderId, long largestChangeId) throws IOException { + private void performIncrementalSync(long largestChangeId) throws IOException { + // Handle 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 driveId : deletedIds) { - deleteDriveFile(driveId, folderId, true); - } - PreferencesUtils.setString( - context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT); - - Map changes = new HashMap(); - largestChangeId = getDriveChangesInfo(folderId, largestChangeId, changes); - - Cursor cursor = null; - try { - // Get all the local tracks with drive file id - cursor = myTracksProviderUtils.getTrackCursor(SyncUtils.DRIVE_ID_TRACKS_QUERY, null, null); - if (cursor != null && cursor.moveToFirst()) { - do { - Track track = myTracksProviderUtils.createTrack(cursor); - String driveId = track.getDriveId(); - - if (changes.containsKey(driveId)) { - - // Track has changed - File driveFile = changes.get(driveId); - if (driveFile == null) { - Log.d(TAG, "Delete local track " + track.getName()); - myTracksProviderUtils.deleteTrack(track.getId()); - } else { - merge(track, driveFile); - } - changes.remove(driveId); - } else { - if (!track.isSharedWithMe()) { - - // Handle the case the track has changed - File driveFile = drive.files().get(driveId).execute(); - if (SyncUtils.isValid(driveFile, folderId)) { - merge(track, driveFile); - } else { - /* - * Track has a drive id, but the drive id is no longer valid. - * E.g., the file is moved to another folder. Clear the drive - * id. - */ - SyncUtils.updateTrack(myTracksProviderUtils, track, null); - } - } - } - } while (cursor.moveToNext()); + if (!PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT.equals(driveDeletedList)) { + String deletedIds[] = TextUtils.split(driveDeletedList, ";"); + for (String driveId : deletedIds) { + deleteDriveFile(driveId, true); } + PreferencesUtils.setString( + context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT); + } - // Insert new tracks from new drive files - insertNewTracks(changes.values()); - PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, largestChangeId); - } finally { - if (cursor != null) { - cursor.close(); + // Handle edited tracks + String driveEditedList = PreferencesUtils.getString( + context, R.string.drive_edited_list_key, PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT); + if (!PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT.equals(driveEditedList)) { + String editedIds[] = TextUtils.split(driveEditedList, ";"); + for (String id : editedIds) { + Track track = myTracksProviderUtils.getTrack(Long.valueOf(id)); + if (track == null) { + continue; + } + if (track.isSharedWithMe()) { + continue; + } + String driveId = track.getDriveId(); + if (driveId == null || driveId.equals("")) { + continue; + } + File driveFile = drive.files().get(driveId).execute(); + if (SyncUtils.isInMyTracksAndValid(driveFile, getFolderId())) { + merge(track, driveFile); + } + } + PreferencesUtils.setString( + context, R.string.drive_edited_list_key, PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT); + } + + // Handle changes from Google Drive + Map changes = new HashMap(); + long newLargestChangeId = getDriveChangesInfo(largestChangeId, changes); + if (newLargestChangeId != largestChangeId) { + Cursor cursor = null; + try { + // Get all the local tracks with drive file id + cursor = myTracksProviderUtils.getTrackCursor(SyncUtils.DRIVE_ID_TRACKS_QUERY, null, null); + if (cursor != null && cursor.moveToFirst()) { + do { + Track track = myTracksProviderUtils.createTrack(cursor); + String driveId = track.getDriveId(); + + if (changes.containsKey(driveId)) { + + // Track has changed + File driveFile = changes.get(driveId); + if (driveFile == null) { + Log.d(TAG, "Delete local track " + track.getName()); + myTracksProviderUtils.deleteTrack(track.getId()); + } else { + if (SyncUtils.isInMyTracksAndValid(driveFile, getFolderId()) + || SyncUtils.isInSharedWithMe(driveFile)) { + merge(track, driveFile); + } else { + SyncUtils.updateTrack(myTracksProviderUtils, track, null); + } + } + changes.remove(driveId); + } + } while (cursor.moveToNext()); + } + + // Insert valid new drive file changes as new tracks + Iterator iterator = changes.keySet().iterator(); + + while (iterator.hasNext()) { + String driveId = iterator.next(); + File file = changes.get(driveId); + if (!SyncUtils.isInMyTracksAndValid(file, getFolderId()) + && !SyncUtils.isInSharedWithMeAndValid(file)) { + iterator.remove(); + } + } + + insertNewTracks(changes.values()); + PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, newLargestChangeId); + } finally { + if (cursor != null) { + cursor.close(); + } } } } /** * Inserts new drive files from tracks without a drive id. - * - * @param folderId the folder id */ - private void insertNewDriveFiles(String folderId) throws IOException { + private void insertNewDriveFiles() throws IOException { Cursor cursor = null; try { cursor = myTracksProviderUtils.getTrackCursor(SyncUtils.NO_DRIVE_ID_TRACKS_QUERY, null, null); @@ -330,7 +366,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { continue; } // If not successful, the next sync will retry again - SyncUtils.insertDriveFile(drive, folderId, context, myTracksProviderUtils, track, true); + SyncUtils.insertDriveFile( + drive, getFolderId(), context, myTracksProviderUtils, track, true); } while (cursor.moveToNext()); } } finally { @@ -350,17 +387,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { if (driveFile == null) { return; } - boolean success = false; - long trackId = -1L; - try { - Uri uri = myTracksProviderUtils.insertTrack(new Track()); - trackId = Long.parseLong(uri.getLastPathSegment()); - success = updateTrack(trackId, driveFile); - } finally { - if (!success && trackId != -1L) { - myTracksProviderUtils.deleteTrack(trackId); - } - } + updateTrack(-1L, driveFile); } } @@ -391,12 +418,11 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Gets the drive changes info in the My Tracks folder, including deleted files. * - * @param folderId the folder id * @param changeId the largest change id * @param changes a map of drive id to file for the changes * @return an updated largest change id */ - private long getDriveChangesInfo(String folderId, long changeId, Map changes) + private long getDriveChangesInfo(long changeId, Map changes) throws IOException { Changes.List request = drive.changes().list().setStartChangeId(changeId + 1); do { @@ -408,12 +434,10 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { changes.put(change.getFileId(), null); } else { File file = change.getFile(); - if (SyncUtils.isInFolder(file, folderId) || SyncUtils.isSharedWithMe(file)) { - if (file.getLabels().getTrashed()) { - changes.put(change.getFileId(), null); - } else { - changes.put(change.getFileId(), file); - } + if (file.getLabels().getTrashed()) { + changes.put(change.getFileId(), null); + } else { + changes.put(change.getFileId(), file); } } } @@ -449,8 +473,12 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { + driveFile.getTitle()); if (!updateTrack(track.getId(), driveFile)) { Log.e(TAG, "Unable to update drive change"); - track.setModifiedTime(driveModifiedTime); - myTracksProviderUtils.updateTrack(track); + // The track could have been deleted in the unsuccessful update + track = myTracksProviderUtils.getTrack(track.getId()); + if (track != null) { + track.setModifiedTime(driveModifiedTime); + myTracksProviderUtils.updateTrack(track); + } } } } @@ -458,7 +486,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Updates a track based on a drive file. Returns true if successful. * - * @param trackId the track id + * @param trackId the track id. -1L to insert a new track * @param driveFile the drive file */ private boolean updateTrack(long trackId, File driveFile) throws IOException { @@ -501,7 +529,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { /** * Imports drive file to track. * - * @param trackId the track id + * @param trackId the track id. -1L to insert a new track * @param driveFile the drive file */ private Track importDriveFile(long trackId, File driveFile) throws IOException { @@ -509,14 +537,17 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { try { inputStream = downloadDriveFile(driveFile, true); if (inputStream == null) { - Log.e(TAG, - "Unable to import file. Input stream is null for drive file " + driveFile.getTitle()); + Log.e(TAG, "Unable to import drive file. Input stream is null."); return null; } TrackImporter trackImporter; boolean useKmz = KmzTrackExporter.KMZ_EXTENSION.equals(driveFile.getFileExtension()); if (useKmz) { + if (trackId == -1L) { + Uri uri = myTracksProviderUtils.insertTrack(new Track()); + trackId = Long.parseLong(uri.getLastPathSegment()); + } trackImporter = new KmzTrackImporter(context, trackId); } else { trackImporter = new KmlFileTrackImporter(context, trackId); @@ -524,18 +555,18 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { long importedId = trackImporter.importFile(inputStream); if (importedId == -1L) { - Log.e(TAG, "Unable to merge, imported id is -1L"); + Log.e(TAG, "Unable to import drive file. Imported id is -1L."); return null; } Track track = myTracksProviderUtils.getTrack(importedId); if (track == null) { - Log.e(TAG, "Unable to merge, imported track is null"); + Log.e(TAG, "Unable to import drive file. Imported track is null."); return null; } else { return track; } } catch (IOException e) { - Log.e(TAG, "Unable to merge", e); + Log.e(TAG, "Unable to import drive file.", e); return null; } finally { if (inputStream != null) { @@ -548,19 +579,19 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { * Deletes a drive file. * * @param driveId the drive id - * @param folderId the folder id * @param canRetry true if can retry the request + * @throws IOException */ - private void deleteDriveFile(String driveId, String folderId, boolean canRetry) - throws UserRecoverableAuthIOException { + private void deleteDriveFile(String driveId, boolean canRetry) + throws IOException { try { File driveFile = drive.files().get(driveId).execute(); - if (SyncUtils.isInFolder(driveFile, folderId)) { + if (SyncUtils.isInMyTracks(driveFile, getFolderId())) { if (!driveFile.getLabels().getTrashed()) { drive.files().trash(driveId).execute(); } // if trashed, ignore - } else if (SyncUtils.isSharedWithMe(driveFile)) { + } else if (SyncUtils.isInSharedWithMe(driveFile)) { if (!driveFile.getLabels().getTrashed()) { drive.files().delete(driveId).execute(); } @@ -570,7 +601,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { throw e; } catch (IOException e) { if (canRetry) { - deleteDriveFile(driveId, folderId, false); + deleteDriveFile(driveId, false); return; } Log.e(TAG, "Unable to delete Drive file for " + driveId, e); 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 7e110b56f..b28dd7a33 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 @@ -47,13 +47,9 @@ import android.database.Cursor; import android.os.Bundle; 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; @@ -189,6 +185,9 @@ public class SyncUtils { PreferencesUtils.setLong(context, R.string.drive_largest_change_id_key, PreferencesUtils.DRIVE_LARGEST_CHANGE_ID_DEFAULT); + PreferencesUtils.setString( + context, R.string.drive_edited_list_key, PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT); + // Clear the drive_deleted_list_key last PreferencesUtils.setString( context, R.string.drive_deleted_list_key, PreferencesUtils.DRIVE_DELETED_LIST_DEFAULT); @@ -226,40 +225,13 @@ public class SyncUtils { return drive.files().insert(file).execute(); } - /** - * Returns true if a drive file is a Shared with me KML or KMZ file. - * - * @param driveFile the drive file - */ - public static boolean isSharedWithMe(File driveFile) { - if (driveFile == null) { - return false; - } - String mimeType = driveFile.getMimeType(); - if (!SyncUtils.KML_MIME_TYPE.equals(mimeType) && !SyncUtils.KMZ_MIME_TYPE.equals(mimeType)) { - return false; - } - return driveFile.getSharedWithMeDate() != null; - } - - /** - * Returns true if a drive file is a KML or KMZ file in the My Tracks folder - * and not trashed. - * - * @param driveFile the drive file - * @param folderId the My Tracks folder id - */ - public static boolean isValid(File driveFile, String folderId) { - return isInFolder(driveFile, folderId) && !driveFile.getLabels().getTrashed(); - } - /** * Returns true if a drive file is a KML or KMZ file in the My Tracks folder. * * @param driveFile the drive file * @param folderId the My Tracks folder id */ - public static boolean isInFolder(File driveFile, String folderId) { + public static boolean isInMyTracks(File driveFile, String folderId) { if (driveFile == null) { return false; } @@ -279,6 +251,44 @@ public class SyncUtils { return false; } + /** + * Returns true if a drive file is a KML or KMZ file in the My Tracks folder + * and not trashed. + * + * @param driveFile the drive file + * @param folderId the My Tracks folder id + */ + public static boolean isInMyTracksAndValid(File driveFile, String folderId) { + return isInMyTracks(driveFile, folderId) && !driveFile.getLabels().getTrashed(); + } + + /** + * Returns true if a drive file is a KML or KMZ file in the Shared with me + * directory. + * + * @param driveFile the drive file + */ + public static boolean isInSharedWithMe(File driveFile) { + if (driveFile == null) { + return false; + } + String mimeType = driveFile.getMimeType(); + if (!SyncUtils.KML_MIME_TYPE.equals(mimeType) && !SyncUtils.KMZ_MIME_TYPE.equals(mimeType)) { + return false; + } + return driveFile.getSharedWithMeDate() != null; + } + + /** + * Returns true if a drive file is a KML or KMZ file in the Shared with me + * directory and is not trashed. + * + * @param driveFile the drive file + */ + public static boolean isInSharedWithMeAndValid(File driveFile) { + return isInSharedWithMe(driveFile) && !driveFile.getLabels().getTrashed(); + } + /** * Inserts a drive file using info from a track. * @@ -372,25 +382,14 @@ public class SyncUtils { try { file = SyncUtils.getTempFile(context, myTracksProviderUtils, track, true); - if (file == null) { Log.e(TAG, "Unable to update drive file. File is null for track " + track.getName()); return false; } String title = track.getName() + "." + KmzTrackExporter.KMZ_EXTENSION; - File updatedFile; - String digest = md5(file); - if (digest != null && digest.equals(driveFile.getMd5Checksum())) { - if (title.equals(driveFile.getTitle())) { - updatedFile = driveFile; - } else { - // Only update the title - updatedFile = updateDriveFile(drive, driveFile, title, null, canRetry); - } - } else { - updatedFile = updateDriveFile(drive, driveFile, title, file, canRetry); - } + File 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()); @@ -513,52 +512,4 @@ public class SyncUtils { : ""); myTracksProviderUtils.updateTrack(track); } - - /** - * Gets the md5 digest for a 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; - } - } - } - } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java index 1a683ae00..04dd33130 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java @@ -143,7 +143,7 @@ public class CalorieUtils { double power = earthGravity * weight * speed * (K1 + grade) + K2 * (speed * speed * speed); // Get the calories in kcal - return power * timeUsed / UnitConversions.KCAL_TO_J; + return power * timeUsed * UnitConversions.J_TO_KCAL; } /** diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtils.java index 5976d3bf1..9bca36f74 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtils.java @@ -15,6 +15,7 @@ */ package com.google.android.apps.mytracks.util; +import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -94,6 +95,7 @@ public class EulaUtils { return sharedPreferences.getBoolean(key, defaultValue); } + @SuppressLint("CommitPrefEdits") private static void setValue(Context context, String key, boolean value) { SharedPreferences sharedPreferences = context.getSharedPreferences( EULA_PREFERENCE_FILE, Context.MODE_PRIVATE); diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java index 602aa3799..6f1d6cff4 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java @@ -26,6 +26,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.text.TextUtils; /** * Utilities to access preferences stored in {@link SharedPreferences}. @@ -60,6 +61,7 @@ public class PreferencesUtils { public static final String DEFAULT_ACTIVITY_DEFAULT = ""; public static final String DRIVE_DELETED_LIST_DEFAULT = ""; + public static final String DRIVE_EDITED_LIST_DEFAULT = ""; public static final long DRIVE_LARGEST_CHANGE_ID_DEFAULT = -1L; public static final boolean DRIVE_SYNC_DEFAULT = false; @@ -278,4 +280,27 @@ public class PreferencesUtils { return PreferencesUtils.CHART_X_AXIS_DEFAULT.equals( getString(context, R.string.chart_x_axis_key, PreferencesUtils.CHART_X_AXIS_DEFAULT)); } + + /** + * Adds a value to a list. + * + * @param context the context + * @param keyId the key id + * @param defaultValue the default value + * @param value the value + */ + public static void addToList(Context context, int keyId, String defaultValue, String value) { + String list = getString(context, keyId, defaultValue); + if (defaultValue.equals(list)) { + setString(context, keyId, value); + return; + } + String[] items = TextUtils.split(list, ";"); + for (String item : items) { + if (value.equals(item)) { + return; + } + } + setString(context, keyId, list + ";" + value); + } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java index f3f161669..368727cf2 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java @@ -46,7 +46,7 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the incorrect values of sensor in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])} + * {@link ChartFragment#fillDataPoint(Location, double[])} */ public void testFillDataPoint_sensorIncorrect() { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); @@ -83,7 +83,7 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the correct values of sensor in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])}. + * {@link ChartFragment#fillDataPoint(Location, double[])}. */ public void testFillDataPoint_sensorCorrect() { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); @@ -118,7 +118,7 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the value of metric Distance in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])}. + * {@link ChartFragment#fillDataPoint(Location, double[])}. */ public void testFillDataPoint_distanceMetric() { // By distance. @@ -159,7 +159,7 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the value of imperial Distance in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])}. + * {@link ChartFragment#fillDataPoint(Location, double[])}. */ public void testFillDataPoint_distanceImperial() { // By distance. @@ -204,7 +204,7 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the values of time in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])}. + * {@link ChartFragment#fillDataPoint(Location, double[])}. */ public void testFillDataPoint_time() { // By time @@ -220,9 +220,9 @@ public class ChartFragmentTest extends AndroidTestCase { } /** - * Tests the logic to get the value of elevation in - * {@link ChartFragment#fillDataPoint(android.content.Context, - * Location, double[])} by one and two points. + * Tests the logic to get the value of elevation in + * {@link ChartFragment#fillDataPoint(Location, double[])} by one and two + * points. */ public void testFillDataPoint_elevation() { MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation(); @@ -247,10 +247,9 @@ public class ChartFragmentTest extends AndroidTestCase { /** * Tests the logic to get the value of speed in - * {@link ChartFragment#fillDataPoint(android.content.Context, Location, double[])}. In this test, - * firstly remove all points in memory, - * and then fill in two points one by one. The speed values of these points - * are 129, 130. + * {@link ChartFragment#fillDataPoint(Location, double[])}. In this test, + * firstly remove all points in memory, and then fill in two points one by + * one. The speed values of these points are 129, 130. */ public void testFillDataPoint_speed() { /* @@ -359,7 +358,7 @@ public class ChartFragmentTest extends AndroidTestCase { */ private double[] fillDataPointTestHelper(Location location) { double[] point = new double[ChartView.NUM_SERIES + 1]; - chartFragment.fillDataPoint(location, point); + chartFragment.fillDataPoint(location, point, getContext()); return point; } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/GpxFileTrackImporterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/GpxFileTrackImporterTest.java index 4eeff4f7b..110e49817 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/GpxFileTrackImporterTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/GpxFileTrackImporterTest.java @@ -20,7 +20,8 @@ import static com.google.android.testing.mocking.AndroidMock.eq; import static com.google.android.testing.mocking.AndroidMock.expect; import com.google.android.apps.mytracks.content.Track; -import com.google.android.apps.mytracks.io.file.importer.GpxFileTrackImporter; +import com.google.android.apps.mytracks.util.PreferencesUtils; +import com.google.android.maps.mytracks.R; import com.google.android.testing.mocking.AndroidMock; import android.location.Location; @@ -97,7 +98,9 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { expect(myTracksProviderUtils.bulkInsertTrackPoint( LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1); - + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); expectUpdateTrack(track, true, TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); @@ -125,12 +128,13 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { expect(myTracksProviderUtils.insertTrack((Track) AndroidMock.anyObject())) .andReturn(TRACK_ID_0_URI); expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0); - // A flush happens at the end expect(myTracksProviderUtils.bulkInsertTrackPoint( (Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3); - + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); expectUpdateTrack(track, true, TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); @@ -166,7 +170,9 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { expect(myTracksProviderUtils.bulkInsertTrackPoint( (Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3); - + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); expectUpdateTrack(track, true, TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); @@ -235,6 +241,9 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { .andStubReturn(1); expect(myTracksProviderUtils.getFirstTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0); + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); myTracksProviderUtils.deleteTrack(TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/KmlFileTrackImporterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/KmlFileTrackImporterTest.java index 358d84316..1f11c5083 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/KmlFileTrackImporterTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/file/importer/KmlFileTrackImporterTest.java @@ -20,6 +20,8 @@ import static com.google.android.testing.mocking.AndroidMock.eq; import static com.google.android.testing.mocking.AndroidMock.expect; import com.google.android.apps.mytracks.content.Track; +import com.google.android.apps.mytracks.util.PreferencesUtils; +import com.google.android.maps.mytracks.R; import com.google.android.testing.mocking.AndroidMock; import android.location.Location; @@ -79,7 +81,9 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter { expect(myTracksProviderUtils.bulkInsertTrackPoint( LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1); - + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); expectUpdateTrack(track, true, TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); @@ -111,7 +115,9 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter { expect(myTracksProviderUtils.bulkInsertTrackPoint( (Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5); expect(myTracksProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3); - + expect( + myTracksProviderUtils.getTrack(PreferencesUtils.getLong(getContext(), + R.string.recording_track_id_key))).andStubReturn(null); expectUpdateTrack(track, true, TRACK_ID_0); AndroidMock.replay(myTracksProviderUtils); diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java index aac24349f..673c7f28c 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java @@ -95,15 +95,18 @@ public class CalorieUtilsTest extends TestCase { } /** - * Checks calculating cycling calorie. + * Checks calculating cycling calorie. 175 is the watt usage for a 90 kg bike + * + rider to go 9 m/s (20 mph or 32 km/h) on the flat. Reference */ public void testCalculateCalorieCycling() { grade = 0; weight = 90; start.setSpeed(9); stop.setSpeed(9); - double expected = 175 / 1000 / UnitConversions.KCAL_TO_J; + double expected = 175.0 * (TIME_INTERVAL / 1000) * UnitConversions.J_TO_KCAL; double actual = CalorieUtils.calculateCalorieCycling(start, stop, grade, weight); - assertEquals(expected, actual); + assertTrue((actual - expected) / expected < 0.02); } } \ No newline at end of file