diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index d515fc273..17a83d830 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -52,6 +52,8 @@ limitations under the License. + + @@ -211,7 +213,6 @@ limitations under the License. android:name="com.google.android.apps.mytracks.io.maps.ChooseMapActivity" /> - diff --git a/MyTracks/res/layout/add_people.xml b/MyTracks/res/layout/add_people.xml new file mode 100644 index 000000000..744a4ed04 --- /dev/null +++ b/MyTracks/res/layout/add_people.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/MyTracks/res/layout/add_people_item.xml b/MyTracks/res/layout/add_people_item.xml new file mode 100644 index 000000000..468bd3b36 --- /dev/null +++ b/MyTracks/res/layout/add_people_item.xml @@ -0,0 +1,32 @@ + + + + + + \ No newline at end of file diff --git a/MyTracks/res/menu/map.xml b/MyTracks/res/menu/map.xml index 0ee97b8e2..34ac287e8 100644 --- a/MyTracks/res/menu/map.xml +++ b/MyTracks/res/menu/map.xml @@ -17,7 +17,7 @@ limitations under the License. diff --git a/MyTracks/res/menu/track_detail.xml b/MyTracks/res/menu/track_detail.xml index 82b64f253..784eed96d 100644 --- a/MyTracks/res/menu/track_detail.xml +++ b/MyTracks/res/menu/track_detail.xml @@ -35,21 +35,28 @@ limitations under the License. android:title="@string/menu_share"> + + - + @@ -61,36 +68,36 @@ limitations under the License. \ No newline at end of file diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml index 29c819b1e..2b0024241 100644 --- a/MyTracks/res/values/keys.xml +++ b/MyTracks/res/values/keys.xml @@ -30,6 +30,9 @@ limitations under the License. chartShowHeartRate chartShowPower chartShowSpeed + confirmPlayEarth + confirmShareDrive + confirmShareMap defaultActivity defaultMapPublic driveSync @@ -45,9 +48,7 @@ limitations under the License. sendToDrive sendToFusionTables sendToMaps - sensorType - showConfirmPlayDialog - showConfrimSharingDialog + sensorType splitFrequency statsShowCoordinate statsShowElevation diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index d557d04c8..1f41f0b1b 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -918,7 +918,7 @@ limitations under the License. translation_description="In the 'Help' page, the answer on share visibility."> By default, tracks sent to Google Maps are unlisted, tracks sent to Google Fusion Tables are public, and tracks sent to Google Docs are private. For Google Maps, you can change the - default between public and unlisted via Settings -> Sharing. After uploading tracks, you can + default between public and unlisted via Settings -> Google. After uploading tracks, you can change their visibility in Google Maps, Google Fusion Tables, and Google Docs. Share + + Share via Drive + + Add people + + + Enter email addresses + + + This will upload your track to Google Drive so you can share the file with friends. The file + will be private and only those you have shared with will have access. + + - This will upload your track to Google Maps so that you can share the map url with friends. The - map will be either public or unlisted depending on your setting at Settings -> Sharing. For more - info on public versus unlisted, see %1$s. + This will upload your track to Google Maps so you can share the map url with friends. The map + will be either public or unlisted depending on your setting at Settings -> Google. For more info + on public versus unlisted, see %1$s. trackIds; @@ -128,14 +129,18 @@ abstract class AbstractImporter extends DefaultHandler { * Constructor. * * @param context the context + * @param importTrackId the track id to import to. -1L to import to a new + * track. */ - protected AbstractImporter(Context context) { - this(context, MyTracksProviderUtils.Factory.get(context)); + protected AbstractImporter(Context context, long importTrackId) { + this(context, importTrackId, MyTracksProviderUtils.Factory.get(context)); } @VisibleForTesting - protected AbstractImporter(Context context, MyTracksProviderUtils myTracksProviderUtils) { + protected AbstractImporter( + Context context, long importTrackId, MyTracksProviderUtils myTracksProviderUtils) { this.context = context; + this.importTrackId = importTrackId; this.myTracksProviderUtils = myTracksProviderUtils; this.minRecordingDistance = PreferencesUtils.getInt(context, R.string.min_recording_distance_key, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT); @@ -290,10 +295,20 @@ abstract class AbstractImporter extends DefaultHandler { /** * On track start. */ - protected void onTrackStart() { + protected void onTrackStart() throws SAXException { trackData = new TrackData(); - Uri uri = myTracksProviderUtils.insertTrack(trackData.track); - long trackId = Long.parseLong(uri.getLastPathSegment()); + long trackId; + if (importTrackId == -1L) { + Uri uri = myTracksProviderUtils.insertTrack(trackData.track); + trackId = Long.parseLong(uri.getLastPathSegment()); + } else { + if (trackIds.size() > 0) { + throw new SAXException(createErrorMessage( + "Cannot import more than one track to an existing track " + importTrackId)); + } + trackId = importTrackId; + myTracksProviderUtils.clearTrack(trackId); + } trackIds.add(trackId); trackData.track.setId(trackId); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java index ee1f8fc8f..3f6acc670 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java @@ -52,14 +52,15 @@ public class GpxImporter extends AbstractImporter { * Constructor. * * @param context the context + * @param importTrackId track id to import to. -1L to import to a new track. */ - public GpxImporter(Context context) { - super(context); + public GpxImporter(Context context, long importTrackId) { + super(context, importTrackId); } @VisibleForTesting public GpxImporter(Context context, MyTracksProviderUtils myTracksProviderUtils) { - super(context, myTracksProviderUtils); + super(context, -1L, myTracksProviderUtils); } @Override @@ -117,7 +118,7 @@ public class GpxImporter extends AbstractImporter { } @Override - protected void onTrackStart() { + protected void onTrackStart() throws SAXException { super.onTrackStart(); name = null; description = null; diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportAsyncTask.java index 15404970e..e600b41e6 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportAsyncTask.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/ImportAsyncTask.java @@ -157,8 +157,9 @@ public class ImportAsyncTask extends AsyncTask { */ private boolean importFile(final File file) { try { - AbstractImporter importer = trackFileFormat == TrackFileFormat.KML ? new KmlImporter(context) - : new GpxImporter(context); + AbstractImporter importer = trackFileFormat == TrackFileFormat.KML ? new KmlImporter( + context, -1L) + : new GpxImporter(context, -1L); long trackIds[] = importer.importFile(new FileInputStream(file)); int length = trackIds.length; if (length > 0) { diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/KmlImporter.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/KmlImporter.java index a78c321ec..59dd5b2e4 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/file/KmlImporter.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/KmlImporter.java @@ -71,14 +71,15 @@ public class KmlImporter extends AbstractImporter { * Constructor. * * @param context the context + * @param importTrackId track id to import to. -1L to import to a new track. */ - public KmlImporter(Context context) { - super(context); + public KmlImporter(Context context, long importTrackId) { + super(context, importTrackId); } @VisibleForTesting public KmlImporter(Context context, MyTracksProviderUtils myTracksProviderUtils) { - super(context, myTracksProviderUtils); + super(context, -1L, myTracksProviderUtils); } @Override diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ConfirmSharingActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ConfirmSharingActivity.java deleted file mode 100644 index 73227b0c9..000000000 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/ConfirmSharingActivity.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.google.android.apps.mytracks.io.sendtogoogle; - -import com.google.android.apps.mytracks.util.DialogUtils; -import com.google.android.apps.mytracks.util.IntentUtils; -import com.google.android.apps.mytracks.util.PreferencesUtils; -import com.google.android.apps.mytracks.util.StringUtils; -import com.google.android.maps.mytracks.R; - -import android.app.Activity; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.CheckBox; -import android.widget.TextView; - -/** - * An interstitial to confirm sharing with friends. - * - * @author Jimmy Shih - */ -public class ConfirmSharingActivity extends Activity { - - private static final int DIALOG_ID = 0; - - private SendRequest sendRequest; - private CheckBox checkBox; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - sendRequest = getIntent().getParcelableExtra(SendRequest.SEND_REQUEST_KEY); - if (PreferencesUtils.getBoolean(this, R.string.show_confirm_sharing_dialog_key, - PreferencesUtils.SHOW_CONFIRM_SHARING_DIALOG_DEFAULT)) { - showDialog(DIALOG_ID); - } else { - startNextActivity(); - } - } - - @Override - protected Dialog onCreateDialog(int id) { - if (id != DIALOG_ID) { - return null; - } - View view = getLayoutInflater().inflate(R.layout.confirm_dialog, null); - - TextView textView = (TextView) view.findViewById(R.id.confirm_dialog_message); - textView.setText(StringUtils.getHtml( - this, R.string.share_track_confirm_message, R.string.maps_public_unlisted_url)); - - checkBox = (CheckBox) view.findViewById(R.id.confirm_dialog_check_box); - DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int button) { - PreferencesUtils.setBoolean(ConfirmSharingActivity.this, - R.string.show_confirm_sharing_dialog_key, !checkBox.isChecked()); - startNextActivity(); - } - }; - DialogInterface.OnClickListener cancelListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int button) { - finish(); - } - }; - - Dialog dialog = DialogUtils.createConfirmationDialog( - this, -1, view, okListener, cancelListener); - dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialogInterface) { - finish(); - } - }); - return dialog; - } - - /** - * Starts the next activity for sharing with friends and finishes this - * activity. - */ - private void startNextActivity() { - Intent intent = IntentUtils.newIntent(this, AccountChooserActivity.class) - .putExtra(SendRequest.SEND_REQUEST_KEY, sendRequest); - startActivity(intent); - finish(); - } -} \ No newline at end of file diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendRequest.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendRequest.java index 840ea1c0d..967f7b898 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendRequest.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendRequest.java @@ -33,6 +33,7 @@ public class SendRequest implements Parcelable { private long trackId = -1L; private String sharingAppPackageName = null; private String sharingAppClassName = null; + private String acl = null; private boolean sendDrive = false; private boolean sendMaps = false; private boolean sendFusionTables = false; @@ -93,6 +94,22 @@ public class SendRequest implements Parcelable { this.sharingAppClassName = sharingAppClassName; } + /** + * Gets the email address of the user to share the track with. + */ + public String getAcl() { + return acl; + } + + /** + * Sets the email address of the user to share the track with. + * + * @param acl the email address + */ + public void setAcl(String acl) { + this.acl = acl; + } + /** * True if the user has selected the send to Google Drive option. */ @@ -279,6 +296,7 @@ public class SendRequest implements Parcelable { trackId = in.readLong(); sharingAppPackageName = in.readString(); sharingAppClassName = in.readString(); + acl = in.readString(); sendDrive = in.readByte() == 1; sendMaps = in.readByte() == 1; sendFusionTables = in.readByte() == 1; @@ -302,6 +320,7 @@ public class SendRequest implements Parcelable { out.writeLong(trackId); out.writeString(sharingAppPackageName); out.writeString(sharingAppClassName); + out.writeString(acl); out.writeByte((byte) (sendDrive ? 1 : 0)); out.writeByte((byte) (sendMaps ? 1 : 0)); out.writeByte((byte) (sendFusionTables ? 1 : 0)); 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 e0c932208..637c51929 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 @@ -254,7 +254,7 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { InputStream inputStream = downloadDriveFile(driveFile); // TODO: should retry if inputStream is null if (inputStream != null) { - KmlImporter kmlImporter = new KmlImporter(context); + KmlImporter kmlImporter = new KmlImporter(context, -1L); long[] tracksIds = kmlImporter.importFile(inputStream); if (tracksIds.length == 1) { Track track = myTracksProviderUtils.getTrack(tracksIds[0]); @@ -333,29 +333,20 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter { track.setModifiedTime(driveModifiedTime); myTracksProviderUtils.updateTrack(track); } else { - KmlImporter kmlImporter = new KmlImporter(context); + KmlImporter kmlImporter = new KmlImporter(context, track.getId()); long[] tracksIds = kmlImporter.importFile(inputStream); if (tracksIds.length == 1) { - // Delete track and update the new imported track - track.setDriveId(""); - myTracksProviderUtils.updateTrack(track); - myTracksProviderUtils.deleteTrack(track.getId()); - Track newTrack = myTracksProviderUtils.getTrack(tracksIds[0]); newTrack.setDriveId(driveFile.getId()); newTrack.setModifiedTime(driveModifiedTime); myTracksProviderUtils.updateTrack(newTrack); } else { + /* - * Do not need to retry, the drive file is invalid. Remove imported - * tracks and update the track modified time. + * TODO: Should revert the track back to the original. */ - Log.e(TAG, "Unable to update drive change for track " + track.getName()); - for (int i = 0; i < tracksIds.length; i++) { - myTracksProviderUtils.deleteTrack(tracksIds[i]); - } - track.setModifiedTime(driveModifiedTime); - myTracksProviderUtils.updateTrack(track); + Log.e( + TAG, "Unable to update drive change. Imported size is not 1 for " + track.getName()); } } } 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 ad063a389..53e71a149 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 @@ -295,13 +295,14 @@ public class SyncUtils { * @param drive the drive * @param folderId the folder id * @param track the track + * @return the added drive file id or null. */ - public static boolean addDriveFile(Context context, MyTracksProviderUtils myTracksProviderUtils, + public static String addDriveFile(Context context, MyTracksProviderUtils myTracksProviderUtils, Drive drive, String folderId, Track track) 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()); - return false; + return null; } try { @@ -323,12 +324,13 @@ public class SyncUtils { File uploadedFile = drive.files().insert(newMetaData, fileContent).execute(); if (uploadedFile == null) { Log.e(TAG, "Unable to add Drive file. Uploaded file is null for track " + track.getName()); - return false; + return null; } - track.setDriveId(uploadedFile.getId()); + String id = uploadedFile.getId(); + track.setDriveId(id); track.setModifiedTime(uploadedFile.getModifiedDate().getValue()); myTracksProviderUtils.updateTrack(track); - return true; + return id; } finally { file.delete(); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/settings/GoogleSettingsActivity.java b/MyTracks/src/com/google/android/apps/mytracks/settings/GoogleSettingsActivity.java index cfda6677b..56bfe7cc0 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/settings/GoogleSettingsActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/settings/GoogleSettingsActivity.java @@ -99,7 +99,12 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity { String googleAccount = PreferencesUtils.getString( GoogleSettingsActivity.this, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT); - if (!newGoogleAccount.equals(googleAccount)) { + if (googleAccount == null + || googleAccount.equals(PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT)) { + updateUiByAccountName(newGoogleAccount); + return true; + } + if (!googleAccount.equals(newGoogleAccount)) { Bundle newBundle = new Bundle(); newBundle.putString(ACCOUNT_NAME_KEY, newGoogleAccount); showDialog(DIALOG_CONFIRM_SWITCH_ACCOUNT, newBundle); 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 c20dc8e4e..87a739866 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/PreferencesUtils.java @@ -49,6 +49,11 @@ public class PreferencesUtils { public static final boolean CHART_SHOW_HEART_RATE_DEFAULT = true; public static final boolean CHART_SHOW_POWER_DEFAULT = true; public static final boolean CHART_SHOW_SPEED_DEFAULT = true; + + public static final boolean CONFIRM_PLAY_EARTH_DEFAULT = true; + public static final boolean CONFIRM_SHARE_DRIVE_DEFAULT = true; + public static final boolean CONFIRM_SHARE_MAP_DEFAULT = true; + public static final String DEFAULT_ACTIVITY_DEFAULT = ""; public static final boolean DEFAULT_MAP_PUBLIC_DEFAULT = false; public static final String DRIVE_DELETED_LIST_DEFAULT = ""; @@ -85,8 +90,6 @@ public class PreferencesUtils { public static final boolean SEND_TO_MAPS_DEFAULT = false; public static final String SENSOR_TYPE_DEFAULT = "NONE"; - public static final boolean SHOW_CONFIRM_PLAY_DIALOG_DEFAULT = true; - public static final boolean SHOW_CONFIRM_SHARING_DIALOG_DEFAULT = true; public static final int SPLIT_FREQUENCY_DEFAULT = 0; public static final boolean STATS_SHOW_COORDINATE_DEFAULT = false; public static final boolean STATS_SHOW_ELEVATION_DEFAULT = false; diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtils.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtils.java index 6b9b41d4f..dee6ed262 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtils.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtils.java @@ -40,6 +40,13 @@ public interface MyTracksProviderUtils { */ public static final String AUTHORITY = "com.google.android.maps.mytracks"; + /** + * Clears a track. Removes waypoints and trackpoints. Only keeps the track id. + * + * @param trackId the track id + */ + public void clearTrack(long trackId); + /** * Creates a {@link Track} from a cursor. * diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java index 97903ca17..6f3a7bbb6 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java @@ -50,6 +50,14 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { this.contentResolver = contentResolver; } + @Override + public void clearTrack(long trackId) { + deleteTrackPointsAndWaypoints(trackId); + Track track = new Track(); + track.setId(trackId); + updateTrack(track); + } + @Override public Track createTrack(Cursor cursor) { int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID); @@ -176,6 +184,21 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { @Override public void deleteTrack(long trackId) { + deleteTrackPointsAndWaypoints(trackId); + + // Delete track last since it triggers a database vaccum call + contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", + new String[] { Long.toString(trackId) }); + } + + /** + * Deletes track points and waypoints of a track. Assumes + * {@link TracksColumns#STARTID}, {@link TracksColumns#STOPID}, and + * {@link TracksColumns#NUMPOINTS} will be updated by the caller. + * + * @param trackId the track id + */ + private void deleteTrackPointsAndWaypoints(long trackId) { Track track = getTrack(trackId); if (track != null) { String where = TrackPointsColumns._ID + ">=? AND " + TrackPointsColumns._ID + "<=?"; @@ -185,10 +208,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils { } contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", new String[] { Long.toString(trackId) }); - - // Delete track last since it triggers a database vaccum call - contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", - new String[] { Long.toString(trackId) }); } @Override