Removed Accounts and sync.

This commit is contained in:
Dennis Guse
2019-06-21 00:03:29 +02:00
parent 57bcded2a9
commit 09798c95c2
64 changed files with 168 additions and 3951 deletions
@@ -18,22 +18,13 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.fragments.ConfirmDeleteDialogFragment;
import com.google.android.apps.mytracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
import com.google.android.apps.mytracks.fragments.InstallEarthDialogFragment;
import com.google.android.apps.mytracks.fragments.ShareTrackDialogFragment;
import com.google.android.apps.mytracks.fragments.ShareTrackDialogFragment.ShareTrackCaller;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.io.file.exporter.SaveActivity;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.services.tasks.CheckPermissionAsyncTask;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.maps.mytracks.R;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.widget.Toast;
/**
@@ -42,48 +33,16 @@ import android.widget.Toast;
* {@link SearchListActivity}:
* <p>
* - share track <br>
* - export track to Google services<br>
* - enable sync to Google Drive <br>
* - delete tracks <br>
* - play tracks
*
*
* @author Jimmy Shih
*/
public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActivity
implements ShareTrackCaller, ConfirmDeleteCaller {
public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActivity implements ConfirmDeleteCaller {
private static final String TAG = AbstractMyTracksActivity.class.getSimpleName();
private static final String SEND_REQUEST_KEY = "send_request_key";
private static final int DRIVE_REQUEST_CODE = 0;
private static final int FUSION_TABLES_REQUEST_CODE = 1;
private static final int SPREADSHEETS_REQUEST_CODE = 2;
private static final int DELETE_REQUEST_CODE = 3;
protected static final int GOOGLE_PLAY_SERVICES_REQUEST_CODE = 4;
protected static final int CAMERA_REQUEST_CODE = 5;
private CheckPermissionAsyncTask asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
//sendRequest = savedInstanceState.getParcelable(SEND_REQUEST_KEY);
}
Object retained = getLastCustomNonConfigurationInstance();
if (retained instanceof CheckPermissionAsyncTask) {
asyncTask = (CheckPermissionAsyncTask) retained;
asyncTask.setActivity(this);
}
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
if (asyncTask != null) {
asyncTask.setActivity(null);
}
return asyncTask;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@@ -95,16 +54,6 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
}
}
/**
* Shares a track.
*
* @param trackId the track id
*/
protected void shareTrack(long trackId) {
ShareTrackDialogFragment.newInstance(trackId)
.show(getSupportFragmentManager(), ShareTrackDialogFragment.SHARE_TRACK_DIALOG_TAG);
}
/**
* Call when not able to get permission for a google service.
*/
@@ -155,22 +104,4 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
* Called after {@link DeleteActivity} returns its result.
*/
abstract protected void onDeleted();
/**
* Play tracks in Google Earth.
*
* @param trackIds the track ids
*/
protected void playTracks(long[] trackIds) {
if (GoogleEarthUtils.isEarthInstalled(this)) {
Intent intent = IntentUtils.newIntent(this, SaveActivity.class)
.putExtra(SaveActivity.EXTRA_TRACK_IDS, trackIds)
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) TrackFileFormat.KML)
.putExtra(SaveActivity.EXTRA_PLAY_TRACK, true);
startActivity(intent);
} else {
new InstallEarthDialogFragment().show(
getSupportFragmentManager(), InstallEarthDialogFragment.INSTALL_EARTH_DIALOG_TAG);
}
}
}
@@ -16,15 +16,15 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
import java.text.NumberFormat;
@@ -16,15 +16,6 @@
package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.StringUtils;
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.content.Intent;
import android.graphics.Canvas;
@@ -36,12 +27,21 @@ import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.VisibleForTesting;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.android.apps.mytracks.stats.ExtremityMonitor;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import java.text.NumberFormat;
import java.util.ArrayList;
@@ -16,16 +16,13 @@ package com.google.android.apps.mytracks;
* the License.
*/
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.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import android.content.Context;
import android.os.AsyncTask;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.util.UnitConversions;
/**
* Async Task to delete tracks.
*
@@ -81,40 +78,16 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
if (trackIds.length == 1 && trackIds[0] == -1L) {
try {
while (SyncUtils.isSyncActive(context)) {
if (isCancelled()) {
return false;
}
Thread.sleep(ONE_SECOND);
}
} catch (InterruptedException e) {
return false;
}
SyncUtils.disableSync(context);
myTracksProviderUtils.deleteAllTracks(context);
return true;
} else {
for (long id : trackIds) {
if (isCancelled()) {
return false;
}
Track track = myTracksProviderUtils.getTrack(id);
myTracksProviderUtils.deleteTrack(context, 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;
}
return true;
}
@Override
@@ -1,132 +0,0 @@
/*
* Copyright 2008 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;
import com.google.android.apps.mytracks.fragments.EulaDialogFragment;
import com.google.android.apps.mytracks.fragments.EulaDialogFragment.EulaCaller;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.GoogleFeedbackUtils;
import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.maps.mytracks.R;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import java.util.Locale;
/**
* An activity that displays the help page.
*
* @author Sandor Dornbush
*/
public class HelpActivity extends AbstractMyTracksActivity implements EulaCaller {
WebView webView;
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String subtitle = getString(R.string.my_tracks_app_name) + " v"
+ SystemUtils.getMyTracksVersion(this);
ApiAdapterFactory.getApiAdapter()
.setTitleAndSubtitle(this, getString(R.string.menu_help), subtitle);
webView = (WebView) findViewById(R.id.help_webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
if (savedInstanceState == null) {
String language = Locale.getDefault().getLanguage();
if (language == null || language.equals("")) {
language = "en";
}
webView.loadUrl(getString(R.string.my_tracks_help_url, language));
} else {
webView.restoreState(savedInstanceState);
}
Button feedback = (Button) findViewById(R.id.help_feedback);
boolean showFeedback = ApiAdapterFactory.getApiAdapter().isGoogleFeedbackAvailable();
feedback.setVisibility(showFeedback ? View.VISIBLE : View.GONE);
if (showFeedback) {
feedback.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GoogleFeedbackUtils.bindFeedback(HelpActivity.this);
}
});
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (webView != null) {
webView.saveState(outState);
}
}
@Override
protected int getLayoutResId() {
return R.layout.help;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.help, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent intent;
switch (item.getItemId()) {
case R.id.help_play_store:
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=com.google.android.maps.mytracks"));
startActivity(intent);
return true;
case R.id.help_eula:
EulaDialogFragment.newInstance(true)
.show(getSupportFragmentManager(), EulaDialogFragment.EULA_DIALOG_TAG);
return true;
case R.id.help_forum:
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(
"http://groups.google.com/a/googleproductforums.com/forum/#!categories/maps/mytracks/"));
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onEulaDone() {
// Do nothing
}
}
@@ -125,18 +125,6 @@ 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();
}
});
@@ -72,8 +72,6 @@ public class MarkerListActivity extends AbstractMyTracksActivity implements Dele
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
boolean isSingleSelection = ids.length == 1;
// Always disable
menu.findItem(R.id.list_context_menu_play).setVisible(false);
// Always disable
menu.findItem(R.id.list_context_menu_share).setVisible(false);
// One item
@@ -149,9 +149,6 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements
isSingleSelectionShareWithMe = false;
isSingleSelectionTrack = false;
}
// Not recording, one item, item is a track
menu.findItem(R.id.list_context_menu_play)
.setVisible(!isRecording && isSingleSelection && isSingleSelectionTrack);
// Not recording, one item, item is a track, not shareWithMe item
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection
&& isSingleSelectionTrack && !isSingleSelectionShareWithMe);
@@ -347,11 +344,9 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements
Long markerId = (Long) item.get(MARKER_ID_FIELD);
Intent intent;
switch (itemId) {
case R.id.list_context_menu_play:
playTracks(new long[] {trackId});
return true;
case R.id.list_context_menu_share:
shareTrack(trackId);
//TODO
Log.e(TAG, "Not implemented");
return true;
case R.id.list_context_menu_show_on_map:
intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
@@ -557,9 +552,4 @@ public class SearchListActivity extends AbstractSendToGoogleActivity implements
}
});
}
@Override
public void onShareTrackDone(long trackId, boolean makePublic, String emails, Account account) {
}
}
@@ -29,9 +29,6 @@ import com.google.android.apps.mytracks.fragments.ExportDialogFragment;
import com.google.android.apps.mytracks.fragments.ExportDialogFragment.ExportCaller;
import com.google.android.apps.mytracks.fragments.ExportDialogFragment.ExportType;
import com.google.android.apps.mytracks.fragments.FrequencyDialogFragment;
import com.google.android.apps.mytracks.fragments.MapLayerDialogFragment;
import com.google.android.apps.mytracks.fragments.PlayMultipleDialogFragment;
import com.google.android.apps.mytracks.fragments.PlayMultipleDialogFragment.PlayMultipleCaller;
import com.google.android.apps.mytracks.fragments.StatsFragment;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.io.file.exporter.SaveActivity;
@@ -80,7 +77,7 @@ import java.util.Date;
* @author Rodrigo Damazio
*/
public class TrackDetailActivity extends AbstractSendToGoogleActivity
implements ChooseActivityTypeCaller, ExportCaller, PlayMultipleCaller {
implements ChooseActivityTypeCaller, ExportCaller {
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id";
@@ -114,7 +111,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
private MenuItem insertMarkerMenuItem;
private MenuItem insertPhotoMenuItem;
private MenuItem playMenuItem;
private MenuItem shareMenuItem;
private MenuItem exportMenuItem;
private MenuItem voiceFrequencyMenuItem;
@@ -357,7 +353,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
insertPhotoMenuItem = menu.findItem(R.id.track_detail_insert_photo);
playMenuItem = menu.findItem(R.id.track_detail_play);
shareMenuItem = menu.findItem(R.id.track_detail_share);
shareMenuItem.setEnabled(!isSharedWithMe);
@@ -402,21 +397,11 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, CAMERA_REQUEST_CODE);
return true;
case R.id.track_detail_play:
playTracks(new long[] {trackId});
return true;
case R.id.track_detail_share:
shareTrack(trackId);
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, trackId);
startActivity(intent);
return true;
case R.id.track_detail_play_multiple:
PlayMultipleDialogFragment.newInstance(trackId)
.show(getSupportFragmentManager(), PlayMultipleDialogFragment.PLAY_MULTIPLE_DIALOG_TAG);
return true;
case R.id.track_detail_voice_frequency:
FrequencyDialogFragment.newInstance(R.string.voice_frequency_key,
PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency)
@@ -449,10 +434,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
intent = IntentUtils.newIntent(this, SettingsActivity.class);
startActivity(intent);
return true;
case R.id.track_detail_help_feedback:
intent = IntentUtils.newIntent(this, HelpActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -470,7 +451,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
return super.onTrackballEvent(event);
}
@Override
public void onExportDone(
ExportType exportType, TrackFileFormat trackFileFormat, Account account) {
if (exportType == ExportType.EXTERNAL_STORAGE) {
@@ -563,9 +543,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
if (insertPhotoMenuItem != null) {
insertPhotoMenuItem.setVisible(hasCamera && isRecording && !isPaused);
}
if (playMenuItem != null) {
playMenuItem.setVisible(!isRecording);
}
if (shareMenuItem != null && shareMenuItem.isEnabled()) {
shareMenuItem.setVisible(!isRecording);
}
@@ -610,22 +587,9 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
@Override
public void onPlayMultipleDone(long[] trackIds) {
playTracks(trackIds);
public void onExportDone(ExportType exportType, TrackFileFormat trackFileFormat) {
//TODO
}
/**
* Shows the map layer dialog.
*/
public void showMapLayerDialog() {
new MapLayerDialogFragment().show(
getSupportFragmentManager(), MapLayerDialogFragment.MAP_LAYER_DIALOG_TAG);
}
@Override
public void onShareTrackDone(long trackId, boolean makePublic, String emails, Account account) {
}
}
@@ -18,24 +18,14 @@ 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.fragments.ChooseAccountDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseAccountDialogFragment.ChooseAccountCaller;
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
import com.google.android.apps.mytracks.fragments.EnableSyncDialogFragment;
import com.google.android.apps.mytracks.fragments.EnableSyncDialogFragment.EnableSyncCaller;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.services.tasks.CheckPermissionAsyncTask;
import com.google.android.apps.mytracks.services.tasks.CheckPermissionAsyncTask.CheckPermissionCaller;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.apps.mytracks.util.TrackUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -48,7 +38,6 @@ import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
/**
* An activity that let's the user see and edit the user editable track meta
@@ -56,8 +45,7 @@ import android.widget.Toast;
*
* @author Leif Hendrik Wilden
*/
public class TrackEditActivity extends AbstractMyTracksActivity implements ChooseActivityTypeCaller,
EnableSyncCaller, ChooseAccountCaller, CheckPermissionCaller {
public class TrackEditActivity extends AbstractMyTracksActivity implements ChooseActivityTypeCaller {
public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_NEW_TRACK = "new_track";
@@ -80,18 +68,10 @@ public class TrackEditActivity extends AbstractMyTracksActivity implements Choos
private boolean newWeight = false;
private CheckPermissionAsyncTask syncDriveAsyncTask;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
Object retained = getLastCustomNonConfigurationInstance();
if (retained instanceof CheckPermissionAsyncTask) {
syncDriveAsyncTask = (CheckPermissionAsyncTask) retained;
syncDriveAsyncTask.setActivity(this);
}
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
if (trackId == -1L) {
@@ -177,19 +157,6 @@ public class TrackEditActivity extends AbstractMyTracksActivity implements Choos
TrackUtils.updateTrack(TrackEditActivity.this, track, name.getText().toString(),
activityType.getText().toString(), description.getText().toString(),
myTracksProviderUtils, trackRecordingServiceConnection, newWeight);
if (EulaUtils.showEnableSync(TrackEditActivity.this)) {
EulaUtils.setShowEnableSync(TrackEditActivity.this);
if (PreferencesUtils.getBoolean(TrackEditActivity.this, R.string.drive_sync_key,
PreferencesUtils.DRIVE_SYNC_DEFAULT)) {
finish();
} else {
new EnableSyncDialogFragment().show(
getSupportFragmentManager(), EnableSyncDialogFragment.ENABLE_SYNC_DIALOG_TAG);
}
} else {
finish();
}
}
});
@@ -209,13 +176,6 @@ public class TrackEditActivity extends AbstractMyTracksActivity implements Choos
}
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
if (syncDriveAsyncTask != null) {
syncDriveAsyncTask.setActivity(null);
}
return syncDriveAsyncTask;
}
@Override
protected void onStart() {
@@ -253,24 +213,4 @@ public class TrackEditActivity extends AbstractMyTracksActivity implements Choos
setActivityTypeIcon(value);
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
}
@Override
public void onEnableSyncDone(boolean enable) {
if (enable) {
new ChooseAccountDialogFragment().show(
getSupportFragmentManager(), ChooseAccountDialogFragment.CHOOSE_ACCOUNT_DIALOG_TAG);
} else {
finish();
}
}
@Override
public void onChooseAccountDone(String account) {
}
@Override
public void onCheckPermissionDone(String scope, boolean success, Intent userRecoverableIntent) {
}
}
@@ -16,42 +16,6 @@
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.TracksColumns;
import com.google.android.apps.mytracks.fragments.ChooseAccountDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseAccountDialogFragment.ChooseAccountCaller;
import com.google.android.apps.mytracks.fragments.ConfirmSyncDialogFragment;
import com.google.android.apps.mytracks.fragments.ConfirmSyncDialogFragment.ConfirmSyncCaller;
import com.google.android.apps.mytracks.fragments.EulaDialogFragment;
import com.google.android.apps.mytracks.fragments.EulaDialogFragment.EulaCaller;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment.FileTypeCaller;
import com.google.android.apps.mytracks.fragments.PlayMultipleDialogFragment;
import com.google.android.apps.mytracks.fragments.PlayMultipleDialogFragment.PlayMultipleCaller;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.io.file.exporter.SaveActivity;
import com.google.android.apps.mytracks.io.file.importer.ImportActivity;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.MyTracksLocationManager;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.settings.SettingsActivity;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.apps.mytracks.util.GoogleLocationUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.ListItemUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.apps.mytracks.util.TrackUtils;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.maps.mytracks.BuildConfig;
import com.google.android.maps.mytracks.R;
import android.accounts.Account;
import android.app.Dialog;
import android.app.SearchManager;
@@ -65,7 +29,6 @@ import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
@@ -84,6 +47,32 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment;
import com.google.android.apps.mytracks.fragments.FileTypeDialogFragment.FileTypeCaller;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.io.file.exporter.SaveActivity;
import com.google.android.apps.mytracks.io.file.importer.ImportActivity;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.services.MyTracksLocationManager;
import com.google.android.apps.mytracks.services.TrackRecordingServiceConnection;
import com.google.android.apps.mytracks.settings.SettingsActivity;
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.GoogleLocationUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.apps.mytracks.util.ListItemUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackRecordingServiceConnectionUtils;
import com.google.android.apps.mytracks.util.TrackUtils;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.maps.mytracks.BuildConfig;
import com.google.android.maps.mytracks.R;
import java.util.Locale;
/**
@@ -91,8 +80,7 @@ import java.util.Locale;
*
* @author Leif Hendrik Wilden
*/
public class TrackListActivity extends AbstractSendToGoogleActivity
implements EulaCaller, FileTypeCaller, PlayMultipleCaller, ChooseAccountCaller, ConfirmSyncCaller {
public class TrackListActivity extends AbstractSendToGoogleActivity implements FileTypeCaller {
private static final String TAG = TrackListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[] { TracksColumns._ID, TracksColumns.NAME,
@@ -181,11 +169,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key == null || key.equals(
PreferencesUtils.getKey(TrackListActivity.this, R.string.drive_sync_key))) {
driveSync = PreferencesUtils.getBoolean(TrackListActivity.this, R.string.drive_sync_key,
PreferencesUtils.DRIVE_SYNC_DEFAULT);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
@@ -217,8 +200,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
isSingleSelectionShareWithMe = false;
}
// Not recording
menu.findItem(R.id.list_context_menu_play).setVisible(!isRecording);
// Not recording, one item, not sharedWithMe item
menu.findItem(R.id.list_context_menu_share)
.setVisible(!isRecording && isSingleSelection && !isSingleSelectionShareWithMe);
@@ -298,13 +279,10 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
private boolean metricUnits = true;
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
private boolean driveSync = PreferencesUtils.DRIVE_SYNC_DEFAULT;
// Menu items
private MenuItem searchMenuItem;
private MenuItem startGpsMenuItem;
private MenuItem playMultipleItem;
private MenuItem syncNowMenuItem;
private MenuItem aggregatedStatisticsMenuItem;
private MenuItem exportAllMenuItem;
private MenuItem importAllMenuItem;
@@ -456,16 +434,10 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_list, menu);
menu.findItem(R.id.track_list_help_feedback).setTitle(
ApiAdapterFactory.getApiAdapter().isGoogleFeedbackAvailable() ? R.string.menu_help_feedback
: R.string.menu_help);
searchMenuItem = menu.findItem(R.id.track_list_search);
ApiAdapterFactory.getApiAdapter().configureSearchWidget(this, searchMenuItem, trackController);
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
playMultipleItem = menu.findItem(R.id.track_list_play_mutliple);
syncNowMenuItem = menu.findItem(R.id.track_list_sync_now);
aggregatedStatisticsMenuItem = menu.findItem(R.id.track_list_aggregated_statistics);
exportAllMenuItem = menu.findItem(R.id.track_list_export_all);
importAllMenuItem = menu.findItem(R.id.track_list_import_all);
@@ -526,18 +498,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
}
myTracksLocationManager.close();
return true;
case R.id.track_list_play_mutliple:
PlayMultipleDialogFragment.newInstance(-1L)
.show(getSupportFragmentManager(), PlayMultipleDialogFragment.PLAY_MULTIPLE_DIALOG_TAG);
return true;
case R.id.track_list_sync_now:
if (driveSync) {
SyncUtils.syncNow(this);
} else {
new ChooseAccountDialogFragment().show(
getSupportFragmentManager(), ChooseAccountDialogFragment.CHOOSE_ACCOUNT_DIALOG_TAG);
}
return true;
case R.id.track_list_aggregated_statistics:
intent = IntentUtils.newIntent(this, AggregatedStatsActivity.class);
startActivity(intent);
@@ -559,10 +519,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
intent = IntentUtils.newIntent(this, SettingsActivity.class);
startActivity(intent);
return true;
case R.id.track_list_help_feedback:
intent = IntentUtils.newIntent(this, HelpActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -638,14 +594,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
* Shows start up dialogs.
*/
public void showStartupDialogs() {
if (!EulaUtils.hasAcceptedEula(this)) {
Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(EulaDialogFragment.EULA_DIALOG_TAG);
if (fragment == null) {
EulaDialogFragment.newInstance(false)
.show(getSupportFragmentManager(), EulaDialogFragment.EULA_DIALOG_TAG);
}
} else {
{
// If stats_units_key is undefined, set it
if (PreferencesUtils.getString(this, R.string.stats_units_key, "").equals("")) {
String statsUnits = getString(
@@ -657,15 +606,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
}
}
@Override
public void onEulaDone() {
if (EulaUtils.hasAcceptedEula(this)) {
showStartupDialogs();
return;
}
finish();
}
private void checkGooglePlayServices() {
int code = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (code != ConnectionResult.SUCCESS) {
@@ -701,12 +641,6 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
TrackIconUtils.setMenuIconColor(startGpsMenuItem);
}
}
if (playMultipleItem != null) {
playMultipleItem.setVisible(hasTrack);
}
if (syncNowMenuItem != null) {
syncNowMenuItem.setTitle(driveSync ? R.string.menu_sync_now : R.string.menu_sync_drive);
}
if (aggregatedStatisticsMenuItem != null) {
aggregatedStatisticsMenuItem.setVisible(hasTrack);
}
@@ -746,11 +680,9 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
*/
private boolean handleContextItem(int itemId, long[] trackIds) {
switch (itemId) {
case R.id.list_context_menu_play:
playTracks(trackIds);
return true;
case R.id.list_context_menu_share:
shareTrack(trackIds[0]);
//TODO
Log.e(TAG, "Not implemented");
return true;
case R.id.list_context_menu_edit:
Intent intent = IntentUtils.newIntent(this, TrackEditActivity.class)
@@ -769,38 +701,7 @@ public class TrackListActivity extends AbstractSendToGoogleActivity
listView.setItemChecked(i, true);
}
return false;
default:
return false;
}
}
@Override
public void onPlayMultipleDone(long[] trackIds) {
playTracks(trackIds);
}
@Override
public void onChooseAccountDone(String account) {
PreferencesUtils.setString(this, R.string.google_account_key, account);
if (PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT.equals(account)) {
return;
} else {
new ConfirmSyncDialogFragment().show(
getSupportFragmentManager(), ConfirmSyncDialogFragment.CONFIRM_SYNC_DIALOG_TAG);
}
}
@Override
public void onConfirmSyncDone(boolean enable) {
if (enable) {
String googleAccount = PreferencesUtils.getString(
this, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
//enableSync(new Account(googleAccount, Constants.ACCOUNT_TYPE));
}
}
@Override
public void onShareTrackDone(long trackId, boolean makePublic, String emails, Account account) {
return false;
}
}
@@ -16,14 +16,14 @@
package com.google.android.apps.mytracks.content;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.ChartURLGenerator;
import com.google.android.apps.mytracks.util.StringUtils;
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 java.util.Vector;
@@ -16,11 +16,6 @@
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -37,9 +32,14 @@ import android.net.Uri;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import java.io.File;
import java.io.FileNotFoundException;
@@ -52,6 +52,7 @@ import java.io.FileNotFoundException;
public class MyTracksProvider extends ContentProvider {
private static final String TAG = MyTracksProvider.class.getSimpleName();
@VisibleForTesting
static final int DATABASE_VERSION = 22;
@@ -16,19 +16,19 @@
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
@@ -16,6 +16,17 @@
package com.google.android.apps.mytracks.fragments;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ZoomControls;
import com.google.android.apps.mytracks.ChartView;
import com.google.android.apps.mytracks.TrackDetailActivity;
import com.google.android.apps.mytracks.content.MyTracksLocation;
@@ -33,17 +44,6 @@ import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ZoomControls;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -1,123 +0,0 @@
/*
* Copyright 2013 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.fragments;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
/**
* A DialogFragment to choose an account.
*
* @author Jimmy Shih
*/
public class ChooseAccountDialogFragment extends AbstractMyTracksDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ChooseAccountCaller {
/**
* Called when choose account is done.
*
* @param account the account chosen
*/
public void onChooseAccountDone(String account);
}
public static final String CHOOSE_ACCOUNT_DIALOG_TAG = "chooseAccount";
private ChooseAccountCaller caller;
private Account[] accounts;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (ChooseAccountCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + ChooseAccountCaller.class.getSimpleName());
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentActivity fragmentActivity = getActivity();
accounts = AccountManager.get(fragmentActivity).getAccountsByType(Constants.ACCOUNT_TYPE);
if (accounts.length == 1) {
dismiss();
caller.onChooseAccountDone(accounts[0].name);
return;
}
}
@Override
protected Dialog createDialog() {
if (accounts.length == 0) {
return new AlertDialog.Builder(getActivity()).setMessage(
R.string.send_google_no_account_message).setTitle(R.string.send_google_no_account_title)
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onChooseAccountDone(PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
}
}).create();
}
String[] choices = new String[accounts.length];
for (int i = 0; i < accounts.length; i++) {
choices[i] = accounts[i].name;
}
return new AlertDialog.Builder(getActivity()).setNegativeButton(
R.string.generic_cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onChooseAccountDone(PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
}
}).setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
caller.onChooseAccountDone(accounts[position].name);
}
}).setSingleChoiceItems(choices, 0, null).setTitle(R.string.send_google_choose_account_title)
.create();
}
@Override
public void onCancel(DialogInterface dialog) {
caller.onChooseAccountDone(PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
}
}
@@ -84,18 +84,10 @@ public class ConfirmDeleteDialogFragment extends AbstractMyTracksDialogFragment
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
int titleId;
int messageId;
if (trackIds.length == 1 && trackIds[0] == -1L) {
boolean driveSync = PreferencesUtils.getBoolean(
getActivity(), R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
titleId = R.string.generic_delete_all_confirm_title;
messageId = driveSync ? R.string.track_delete_all_confirm_message_sync_on
: R.string.track_delete_all_confirm_message_sync_off;
} else {
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title
: R.string.track_delete_one_confirm_title;
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message
: R.string.track_delete_one_confirm_message;
}
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title
: R.string.track_delete_one_confirm_title;
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message
: R.string.track_delete_one_confirm_message;
return DialogUtils.createConfirmationDialog(
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
@Override
@@ -1,89 +0,0 @@
/*
* Copyright 2013 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.fragments;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.support.v4.app.FragmentActivity;
/**
* A DialogFrament to confirm sync to Google Drive.
*
* @author Jimmy Shih
*/
public class ConfirmSyncDialogFragment extends AbstractMyTracksDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ConfirmSyncCaller {
/**
* Called when confirm sync is done.
*/
public void onConfirmSyncDone(boolean enable);
}
public static final String CONFIRM_SYNC_DIALOG_TAG = "confirmSyncDialog";
private ConfirmSyncCaller caller;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (ConfirmSyncCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + ConfirmSyncCaller.class.getSimpleName());
}
}
@Override
protected Dialog createDialog() {
FragmentActivity fragmentActivity = getActivity();
String googleAccount = PreferencesUtils.getString(
fragmentActivity, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
String message = getString(
R.string.sync_drive_confirm_message, googleAccount, getString(R.string.my_tracks_app_name));
return new AlertDialog.Builder(fragmentActivity).setMessage(message)
.setNegativeButton(R.string.generic_no, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onConfirmSyncDone(false);
}
}).setPositiveButton(R.string.generic_yes, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onConfirmSyncDone(true);
}
}).setTitle(R.string.sync_drive_confirm_title).create();
}
@Override
public void onCancel(DialogInterface arg0) {
caller.onConfirmSyncDone(false);
}
}
@@ -1,148 +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.fragments;
import com.google.android.apps.mytracks.util.EulaUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.KeyEvent;
import android.widget.TextView;
/**
* A DialogFragment to show EULA.
*
* @author Jimmy Shih
*/
public class EulaDialogFragment extends AbstractMyTracksDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface EulaCaller {
/**
* Called when eula is done.
*/
public void onEulaDone();
}
public static final String EULA_DIALOG_TAG = "eulaDialog";
private static final String KEY_HAS_ACCEPTED = "hasAccepted";
private static final String GOOGLE_URL = "m.google.com";
private static final String KOREAN = "ko";
/**
* Creates a new instance of {@link EulaDialogFragment}.
*
* @param hasAccepted true if the user has accepted the eula.
*/
public static EulaDialogFragment newInstance(boolean hasAccepted) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_HAS_ACCEPTED, hasAccepted);
EulaDialogFragment eulaDialogFragment = new EulaDialogFragment();
eulaDialogFragment.setArguments(bundle);
return eulaDialogFragment;
}
private EulaCaller caller;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (EulaCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + EulaCaller.class.getSimpleName());
}
}
@Override
protected Dialog createDialog() {
boolean hasAccepted = getArguments().getBoolean(KEY_HAS_ACCEPTED);
SpannableString message = new SpannableString(getEulaText());
Linkify.addLinks(message, Linkify.WEB_URLS);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setMessage(message)
.setTitle(R.string.menu_help_eula);
if (hasAccepted) {
builder.setPositiveButton(R.string.generic_ok, null);
} else {
builder.setNegativeButton(R.string.eula_decline, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onEulaDone();
}
}).setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
return true;
}
return false;
}
}).setPositiveButton(R.string.eula_accept, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
EulaUtils.setAcceptedEula(getActivity());
caller.onEulaDone();
}
});
}
return builder.create();
}
@Override
public void onStart() {
super.onStart();
TextView textView = (TextView) getDialog().findViewById(android.R.id.message);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setTextAppearance(getActivity(), R.style.TextSmall);
}
@Override
public void onCancel(DialogInterface arg0) {
caller.onEulaDone();
}
/**
* Gets the EULA text.
*/
private String getEulaText() {
String tos = getString(R.string.eula_date) + "\n\n" + getString(R.string.eula_body, GOOGLE_URL)
+ "\n\n" + getString(R.string.eula_footer, GOOGLE_URL) + "\n\n"
+ getString(R.string.eula_copyright_year);
boolean isKorean = getResources().getConfiguration().locale.getLanguage().equals(KOREAN);
if (isKorean) {
tos += "\n\n" + getString(R.string.eula_korean);
}
return tos;
}
}
@@ -18,7 +18,6 @@ package com.google.android.apps.mytracks.fragments;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.util.AccountUtils;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
@@ -53,10 +52,6 @@ public class ExportDialogFragment extends AbstractMyTracksDialogFragment {
* @author Jimmy Shih
*/
public enum ExportType {
GOOGLE_DRIVE(R.string.export_google_drive),
GOOGLE_MAPS(R.string.export_google_maps),
GOOGLE_FUSION_TABLES(R.string.export_google_fusion_tables),
GOOGLE_SPREADSHEET(R.string.export_google_spreadsheets),
EXTERNAL_STORAGE(R.string.export_external_storage);
final int resId;
@@ -77,35 +72,24 @@ public class ExportDialogFragment extends AbstractMyTracksDialogFragment {
*
* @param exportType the export type
* @param trackFileFormat the track file format
* @param account the account
*/
public void onExportDone(
ExportType exportType, TrackFileFormat trackFileFormat, Account account);
ExportType exportType, TrackFileFormat trackFileFormat);
}
public static final String EXPORT_DIALOG_TAG = "export";
private static final String KEY_HIDE_DRIVE = "hideDrive";
public static ExportDialogFragment newInstance(boolean hideDrive) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_HIDE_DRIVE, hideDrive);
ExportDialogFragment fragment = new ExportDialogFragment();
fragment.setArguments(bundle);
return fragment;
//TODO REMOVE
return new ExportDialogFragment();
}
private ExportCaller caller;
private Account[] accounts;
private ArrayList<ExportType> exportTypeOptionsList;
// UI elements
private Spinner exportTypeOptions;
private RadioGroup exportGoogleMapsOptions;
private RadioGroup exportGoogleFusionTablesOptions;
private RadioGroup exportExternalStorageOptions;
private Spinner accountSpinner;
@Override
public void onAttach(Activity activity) {
@@ -121,34 +105,10 @@ public class ExportDialogFragment extends AbstractMyTracksDialogFragment {
@Override
protected Dialog createDialog() {
FragmentActivity fragmentActivity = getActivity();
accounts = AccountManager.get(fragmentActivity).getAccountsByType(Constants.ACCOUNT_TYPE);
// Get views
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.export, null);
exportTypeOptions = (Spinner) view.findViewById(R.id.export_type_options);
exportGoogleMapsOptions = (RadioGroup) view.findViewById(R.id.export_google_maps_options);
exportGoogleFusionTablesOptions = (RadioGroup) view.findViewById(
R.id.export_google_fusion_tables_options);
exportExternalStorageOptions = (RadioGroup) view.findViewById(
R.id.export_external_storage_options);
accountSpinner = (Spinner) view.findViewById(R.id.export_account);
// Setup exportTypeOptions
setupExportTypeOptions(fragmentActivity);
// Setup exportGoogleMapsOptions
boolean exportGoogleMapsPublic = PreferencesUtils.getBoolean(fragmentActivity,
R.string.export_google_maps_public_key, PreferencesUtils.EXPORT_GOOGLE_MAPS_PUBLIC_DEFAULT);
exportGoogleMapsOptions.check(
exportGoogleMapsPublic ? R.id.export_google_maps_public : R.id.export_google_maps_unlisted);
// Setup exportGoogleFusionTablesOptions
boolean exportGoogleFusionTablesPublic = PreferencesUtils.getBoolean(fragmentActivity,
R.string.export_google_fusion_tables_public_key,
PreferencesUtils.EXPORT_GOOGLE_FUSION_TABLES_PUBLIC_DEFAULT);
exportGoogleFusionTablesOptions.check(
exportGoogleFusionTablesPublic ? R.id.export_google_fusion_tables_public
: R.id.export_google_fusion_tables_private);
// Setup exportExternalStorageOptions
setExternalStorageOption(
@@ -164,100 +124,26 @@ public class ExportDialogFragment extends AbstractMyTracksDialogFragment {
PreferencesUtils.EXPORT_EXTERNAL_STORAGE_FORMAT_DEFAULT));
exportExternalStorageOptions.check(getExternalStorageFormatId(trackFileFormat));
// Setup accountSpinner
AccountUtils.setupAccountSpinner(fragmentActivity, accountSpinner, accounts);
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
R.string.generic_cancel, null)
.setPositiveButton(R.string.menu_export, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FragmentActivity context = getActivity();
ExportType type = exportTypeOptionsList.get(
exportTypeOptions.getSelectedItemPosition());
TrackFileFormat format = null;
return new AlertDialog.Builder(fragmentActivity)
.setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.menu_export, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FragmentActivity context = getActivity();
ExportType type = exportTypeOptionsList.get(
exportTypeOptions.getSelectedItemPosition());
TrackFileFormat format = null;
PreferencesUtils.setString(context, R.string.export_type_key, type.name());
if (type == ExportType.GOOGLE_MAPS) {
PreferencesUtils.setBoolean(context, R.string.export_google_maps_public_key,
exportGoogleMapsOptions.getCheckedRadioButtonId()
== R.id.export_google_maps_public);
} else if (type == ExportType.GOOGLE_FUSION_TABLES) {
PreferencesUtils.setBoolean(context, R.string.export_google_fusion_tables_public_key,
exportGoogleFusionTablesOptions.getCheckedRadioButtonId()
== R.id.export_google_fusion_tables_public);
} else if (type == ExportType.EXTERNAL_STORAGE) {
format = getTrackFileFormat(exportExternalStorageOptions.getCheckedRadioButtonId());
PreferencesUtils.setString(
context, R.string.export_external_storage_format_key, format.name());
}
Account account;
if (accounts.length == 0) {
account = null;
} else if (accounts.length == 1) {
account = accounts[0];
} else {
account = accounts[accountSpinner.getSelectedItemPosition()];
}
AccountUtils.updateShareTrackAccountPreference(context, account);
caller.onExportDone(type, format, account);
}
}).setTitle(R.string.export_title).setView(view).create();
}
private void setupExportTypeOptions(FragmentActivity fragmentActivity) {
boolean hideDrive = getArguments().getBoolean(KEY_HIDE_DRIVE);
ExportType exportType = ExportType.valueOf(PreferencesUtils.getString(
fragmentActivity, R.string.export_type_key, PreferencesUtils.EXPORT_TYPE_DEFAULT));
if (hideDrive && exportType == ExportType.GOOGLE_DRIVE) {
exportType = ExportType.GOOGLE_MAPS;
}
exportTypeOptionsList = new ArrayList<ExportDialogFragment.ExportType>();
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
getActivity(), android.R.layout.simple_spinner_item);
if (accounts.length > 0) {
if (!hideDrive) {
exportTypeOptionsList.add(ExportType.GOOGLE_DRIVE);
}
exportTypeOptionsList.add(ExportType.GOOGLE_MAPS);
exportTypeOptionsList.add(ExportType.GOOGLE_FUSION_TABLES);
exportTypeOptionsList.add(ExportType.GOOGLE_SPREADSHEET);
}
exportTypeOptionsList.add(ExportType.EXTERNAL_STORAGE);
int selection = 0;
for (int i = 0; i < exportTypeOptionsList.size(); i++) {
ExportType type = exportTypeOptionsList.get(i);
adapter.add(getString(type.resId));
if (type == exportType) {
selection = i;
}
}
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
exportTypeOptions.setAdapter(adapter);
exportTypeOptions.setSelection(selection);
exportTypeOptions.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
ExportType type = exportTypeOptionsList.get(position);
exportGoogleMapsOptions.setVisibility(
type == ExportType.GOOGLE_MAPS ? View.VISIBLE : View.GONE);
exportGoogleFusionTablesOptions.setVisibility(
type == ExportType.GOOGLE_FUSION_TABLES ? View.VISIBLE : View.GONE);
exportExternalStorageOptions.setVisibility(
type == ExportType.EXTERNAL_STORAGE ? View.VISIBLE : View.GONE);
accountSpinner.setVisibility(
accounts.length > 1 && type != ExportType.EXTERNAL_STORAGE ? View.VISIBLE : View.GONE);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Safely ignore
}
});
PreferencesUtils.setString(context, R.string.export_type_key, type.name());
if (type == ExportType.EXTERNAL_STORAGE) {
format = getTrackFileFormat(exportExternalStorageOptions.getCheckedRadioButtonId());
PreferencesUtils.setString(
context, R.string.export_external_storage_format_key, format.name());
}
caller.onExportDone(type, format);
}
})
.setTitle(R.string.export_title).setView(view).create();
}
/**
@@ -1,58 +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.fragments;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
/**
* A DialogFragment to install Google Earth.
*
* @author Jimmy Shih
*/
public class InstallEarthDialogFragment extends AbstractMyTracksDialogFragment {
public static final String INSTALL_EARTH_DIALOG_TAG = "installEarthDialog";
@Override
protected Dialog createDialog() {
return new AlertDialog.Builder(getActivity()).setMessage(
R.string.track_detail_install_earth_message).setNegativeButton(R.string.generic_no, null)
.setPositiveButton(R.string.generic_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent().setData(
Uri.parse(GoogleEarthUtils.GOOGLE_EARTH_MARKET_URL));
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(
getActivity(), R.string.track_detail_install_earth_error, Toast.LENGTH_LONG)
.show();
}
}
}).setTitle(R.string.track_detail_install_earth_title).create();
}
}
@@ -1,71 +0,0 @@
/*
* Copyright 2014 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.fragments;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.maps.mytracks.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
/**
* A DialogFragment to select a map layer.
*
* @author Jimmy Shih
*/
public class MapLayerDialogFragment extends AbstractMyTracksDialogFragment {
public static final String MAP_LAYER_DIALOG_TAG = "mapLayer";
private static final int[] LAYERS = { R.string.menu_map, R.string.menu_satellite,
R.string.menu_satellite_with_streets, R.string.menu_terrain };
private static final int[] MAP_TYPES = { GoogleMap.MAP_TYPE_NORMAL, GoogleMap.MAP_TYPE_SATELLITE,
GoogleMap.MAP_TYPE_HYBRID, GoogleMap.MAP_TYPE_TERRAIN };
@Override
protected Dialog createDialog() {
String[] choices = new String[LAYERS.length];
for (int i = 0; i < LAYERS.length; i++) {
choices[i] = getString(LAYERS[i]);
}
int mapType = PreferencesUtils.getInt(
getActivity(), R.string.map_type_key, PreferencesUtils.MAP_TYPE_DEFAUlT);
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
PreferencesUtils.setInt(getActivity(), R.string.map_type_key, MAP_TYPES[position]);
}
}).setSingleChoiceItems(choices, getPositionFromMapType(mapType), null)
.setTitle(R.string.menu_map_layer).create();
}
private int getPositionFromMapType(int mapType) {
for (int i = 0; i < MAP_TYPES.length; i++) {
if (MAP_TYPES[i] == mapType) {
return i;
}
}
return 0;
}
}
@@ -1,136 +0,0 @@
/*
* Copyright 2013 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.fragments;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.util.TrackUtils;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import java.util.ArrayList;
/**
* A DialogFragment to play multiple tracks.
*
* @author Jimmy Shih
*/
public class PlayMultipleDialogFragment extends AbstractMyTracksDialogFragment {
public static final String PLAY_MULTIPLE_DIALOG_TAG = "playMultipleDialog";
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface PlayMultipleCaller {
/**
* Called when play multiple is done.
*/
public void onPlayMultipleDone(long[] trackIds);
}
private static final String KEY_TRACK_ID = "trackId";
public static PlayMultipleDialogFragment newInstance(long trackId) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_TRACK_ID, trackId);
PlayMultipleDialogFragment fragment = new PlayMultipleDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
private PlayMultipleCaller caller;
private String[] items;
private boolean[] checkedItems;
private long[] trackIds;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (PlayMultipleCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + PlayMultipleCaller.class.getSimpleName());
}
}
@Override
protected Dialog createDialog() {
loadData();
return new AlertDialog.Builder(getActivity()).setMultiChoiceItems(items, checkedItems, null)
.setNegativeButton(R.string.generic_cancel, null)
.setPositiveButton(R.string.menu_play, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
SparseBooleanArray array = ((AlertDialog) dialog).getListView()
.getCheckedItemPositions();
caller.onPlayMultipleDone(getChecked(array));
}
}).setTitle(R.string.menu_play_multiple).create();
}
private long[] getChecked(SparseBooleanArray array) {
ArrayList<Long> checked = new ArrayList<Long>();
for (int i = 0; i < array.size(); i++) {
int key = array.keyAt(i);
if (array.get(key)) {
checked.add(trackIds[key]);
}
}
long[] result = new long[checked.size()];
for (int i = 0; i < checked.size(); i++) {
result[i] = checked.get(i).longValue();
}
return result;
}
private void loadData() {
long trackId = getArguments().getLong(KEY_TRACK_ID);
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(getActivity());
Cursor cursor = myTracksProviderUtils.getTrackCursor(null, null, TrackUtils.TRACK_SORT_ORDER);
if (cursor == null) {
items = new String[] {};
checkedItems = new boolean[] {};
trackIds = new long[] {};
} else {
int count = cursor.getCount();
items = new String[count];
checkedItems = new boolean[count];
trackIds = new long[count];
for (int i = 0; i < count; i++) {
cursor.moveToPosition(i);
Track track = myTracksProviderUtils.createTrack(cursor);
items[i] = track.getName();
checkedItems[i] = trackId != -1L && track.getId() == trackId;
trackIds[i] = track.getId();
}
}
}
}
@@ -1,210 +0,0 @@
/*
* Copyright 2013 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.fragments;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.util.AccountUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.FilterQueryProvider;
import android.widget.MultiAutoCompleteTextView;
import android.widget.Spinner;
import android.widget.Toast;
/**
* A DialogFragment to share a track.
*
* @author Jimmy Shih
*/
public class ShareTrackDialogFragment extends AbstractMyTracksDialogFragment {
/**
* Interface for caller of this dialog fragment.
*
* @author Jimmy Shih
*/
public interface ShareTrackCaller {
/**
* Called when share track is done.
*
* @param trackId the track id
* @param makePublic true to make the track public
* @param emails the emails to share the track with
* @param account the google drive account
*/
public void onShareTrackDone(long trackId, boolean makePublic, String emails, Account account);
}
public static final String SHARE_TRACK_DIALOG_TAG = "shareTrackDialog";
private static final String KEY_TRACK_ID = "trackId";
public static ShareTrackDialogFragment newInstance(long trackId) {
Bundle bundle = new Bundle();
bundle.putLong(KEY_TRACK_ID, trackId);
ShareTrackDialogFragment shareleTrackDialogFragment = new ShareTrackDialogFragment();
shareleTrackDialogFragment.setArguments(bundle);
return shareleTrackDialogFragment;
}
private ShareTrackCaller caller;
private Account[] accounts;
// UI elements
private CheckBox publicCheckBox;
private CheckBox inviteCheckBox;
private MultiAutoCompleteTextView multiAutoCompleteTextView;
private Spinner accountSpinner;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (ShareTrackCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + ShareTrackCaller.class.getSimpleName());
}
}
@Override
protected Dialog createDialog() {
FragmentActivity fragmentActivity = getActivity();
accounts = AccountManager.get(fragmentActivity).getAccountsByType(Constants.ACCOUNT_TYPE);
if (accounts.length == 0) {
return new AlertDialog.Builder(fragmentActivity).setMessage(
R.string.send_google_no_account_message).setTitle(R.string.send_google_no_account_title)
.setPositiveButton(R.string.generic_ok, null).create();
}
// Get all the views
View view = fragmentActivity.getLayoutInflater().inflate(R.layout.share_track, null);
publicCheckBox = (CheckBox) view.findViewById(R.id.share_track_public);
inviteCheckBox = (CheckBox) view.findViewById(R.id.share_track_invite);
multiAutoCompleteTextView = (MultiAutoCompleteTextView) view.findViewById(
R.id.share_track_emails);
accountSpinner = (Spinner) view.findViewById(R.id.share_track_account);
// Setup publicCheckBox
publicCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.share_track_public_key,
PreferencesUtils.SHARE_TRACK_PUBLIC_DEFAULT));
// Setup inviteCheckBox
inviteCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
multiAutoCompleteTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
}
});
inviteCheckBox.setChecked(PreferencesUtils.getBoolean(
fragmentActivity, R.string.share_track_invite_key,
PreferencesUtils.SHARE_TRACK_INVITE_DEFAULT));
// Setup multiAutoCompleteTextView
multiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
SimpleCursorAdapter adapter = new SimpleCursorAdapter(fragmentActivity,
R.layout.add_emails_item, getAutoCompleteCursor(fragmentActivity, null), new String[] {
ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA },
new int[] { android.R.id.text1, android.R.id.text2 }, 0);
adapter.setCursorToStringConverter(new SimpleCursorAdapter.CursorToStringConverter() {
@Override
public CharSequence convertToString(Cursor cursor) {
int index = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
return cursor.getString(index).trim();
}
});
adapter.setFilterQueryProvider(new FilterQueryProvider() {
@Override
public Cursor runQuery(CharSequence constraint) {
return getAutoCompleteCursor(getActivity(), constraint);
}
});
multiAutoCompleteTextView.setAdapter(adapter);
// Setup accountSpinner
accountSpinner.setVisibility(accounts.length > 1 ? View.VISIBLE : View.GONE);
AccountUtils.setupAccountSpinner(fragmentActivity, accountSpinner, accounts);
return new AlertDialog.Builder(fragmentActivity).setNegativeButton(
R.string.generic_cancel, null)
.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FragmentActivity context = getActivity();
if (!publicCheckBox.isChecked() && !inviteCheckBox.isChecked()) {
Toast.makeText(context, R.string.share_track_no_selection, Toast.LENGTH_LONG).show();
return;
}
String acl = multiAutoCompleteTextView.getText().toString().trim();
if (!publicCheckBox.isChecked() && acl.equals("")) {
Toast.makeText(context, R.string.share_track_no_emails, Toast.LENGTH_LONG).show();
return;
}
PreferencesUtils.setBoolean(
context, R.string.share_track_public_key, publicCheckBox.isChecked());
PreferencesUtils.setBoolean(
context, R.string.share_track_invite_key, inviteCheckBox.isChecked());
Account account = accounts.length > 1 ? accounts[accountSpinner
.getSelectedItemPosition()]
: accounts[0];
AccountUtils.updateShareTrackAccountPreference(context, account);
caller.onShareTrackDone(
getArguments().getLong(KEY_TRACK_ID), publicCheckBox.isChecked(), acl, account);
}
}).setTitle(R.string.share_track_title).setView(view).create();
}
/**
* Gets the auto complete cursor.
*
* @param constraint the constraint
*/
private Cursor getAutoCompleteCursor(FragmentActivity fragmentActivity, CharSequence constraint) {
String order = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
if (constraint != null) {
selection += " AND (" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE '%" + constraint
+ "%' OR " + ContactsContract.CommonDataKinds.Email.DATA + " LIKE '%" + constraint
+ "%' )";
}
String[] projection = new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA };
Uri uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
return fragmentActivity.getContentResolver().query(uri, projection, selection, null, order);
}
}
@@ -15,6 +15,12 @@
*/
package com.google.android.apps.mytracks.io.file.exporter;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.DescriptionGenerator;
import com.google.android.apps.mytracks.content.DescriptionGeneratorImpl;
import com.google.android.apps.mytracks.content.MyTracksLocation;
@@ -26,15 +32,8 @@ 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.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import java.io.File;
import java.io.OutputStream;
@@ -188,7 +187,7 @@ public class KmlTrackWriter implements TrackWriter {
@Override
public void writeBeginTracks() {
if (printWriter != null && multiple) {
printWriter.println("<Folder id=\"" + GoogleEarthUtils.TOUR_FEATURE_ID_VALUE + "\">");
printWriter.println("<Folder id=tour>");
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
printWriter.println("<open>1</open>");
}
@@ -206,12 +205,7 @@ public class KmlTrackWriter implements TrackWriter {
if (printWriter != null) {
String name = context.getString(R.string.marker_label_start, track.getName());
writePlacemark(name, "", "", START_STYLE, startLocation);
if (multiple) {
// No need to add TOUR_FEATURE_ID_VALUE
printWriter.println("<Placemark>");
} else {
printWriter.println("<Placemark id=\"" + GoogleEarthUtils.TOUR_FEATURE_ID_VALUE + "\">");
}
printWriter.println("<Placemark>");
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println(
"<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
@@ -19,7 +19,6 @@ package com.google.android.apps.mytracks.io.file.exporter;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.GoogleEarthUtils;
import com.google.android.apps.mytracks.util.IntentUtils;
import com.google.android.maps.mytracks.R;
@@ -200,12 +199,7 @@ public class SaveActivity extends Activity {
totalCount = aTotalCount;
savedPath = aSavedPath;
removeDialog(DIALOG_PROGRESS_ID);
if (playTrack && successCount == 1 && totalCount == 1 && savedPath != null) {
startActivity(GoogleEarthUtils.getPlayInEarthIntent(this, savedPath));
finish();
} else {
showDialog(DIALOG_RESULT_ID);
}
showDialog(DIALOG_RESULT_ID);
}
/**
@@ -16,12 +16,12 @@
package com.google.android.apps.mytracks.io.file.importer;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.location.Location;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@@ -21,11 +21,11 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.content.Waypoint.WaypointType;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.location.Location;
import android.net.Uri;
import android.support.annotation.VisibleForTesting;
import java.util.ArrayList;
@@ -1,604 +0,0 @@
/*
* Copyright 2013 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.sync;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.io.file.exporter.KmzTrackExporter;
import com.google.android.apps.mytracks.io.file.importer.KmlFileTrackImporter;
import com.google.android.apps.mytracks.io.file.importer.KmzTrackImporter;
import com.google.android.apps.mytracks.io.file.importer.TrackImporter;
import com.google.android.apps.mytracks.util.FileUtils;
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;
import com.google.api.services.drive.Drive.Changes;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.model.About;
import com.google.api.services.drive.model.Change;
import com.google.api.services.drive.model.ChangeList;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
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;
/**
* SyncAdapter to sync tracks with Google Drive.
*
* @author Jimmy Shih
*/
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = SyncAdapter.class.getSimpleName();
// drive.about.get fields. Contains one field, largestChangeId
private static final String ABOUT_GET_FIELDS = "largestChangeId";
private final Context context;
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;
this.myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
if (!PreferencesUtils.getBoolean(
context, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT)) {
return;
}
if (account == null) {
return;
}
}
/**
* Gets the folder id..
*/
private String getFolderId() throws IOException {
File folder = SyncUtils.getMyTracksFolder(context, drive);
if (folder == null) {
throw new IOException("folder is null");
}
String id = folder.getId();
if (id == null) {
throw new IOException("folder id is null");
}
return id;
}
/**
* 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));
Map<String, File> myTracksFolderMap = getFiles(myTracksFolderRequest, true);
// Handle tracks that are already uploaded to Google Drive
Set<String> syncedDriveIds = updateSyncedTracks();
for (String driveId : syncedDriveIds) {
myTracksFolderMap.remove(driveId);
}
// Get all the KML/KMZ files in the "Shared with me:/" folder
Files.List sharedWithMeRequest = drive.files()
.list().setQ(SyncUtils.SHARED_WITH_ME_FILES_QUERY);
Map<String, File> sharedWithMeMap = getFiles(sharedWithMeRequest, false);
try {
insertNewTracks(myTracksFolderMap.values());
insertNewTracks(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);
if (!syncedDriveIds.contains(track.getDriveId())) {
myTracksProviderUtils.deleteTrack(context, track.getId());
}
} while (cursor.moveToNext());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
throw e;
}
}
/**
* Updates synced tracks.
*
* @return drive ids of the synced tracks
*/
private Set<String> updateSyncedTracks() throws IOException {
Set<String> result = new HashSet<String>();
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);
String driveId = track.getDriveId();
if (driveId != null && !driveId.equals("")) {
if (!track.isSharedWithMe()) {
File driveFile = drive.files().get(driveId).execute();
if (SyncUtils.isInMyTracksAndValid(driveFile, folderId)) {
merge(track, driveFile);
result.add(driveId);
} 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());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return result;
}
/**
* Performs incremental sync.
*
* @param largestChangeId the largest change id
*/
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);
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);
}
// 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, folderId)) {
merge(track, driveFile);
}
}
PreferencesUtils.setString(
context, R.string.drive_edited_list_key, PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT);
}
// Handle changes from Google Drive
Map<String, File> changes = new HashMap<String, File>();
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(context, track.getId());
} else {
if (SyncUtils.isInMyTracksAndValid(driveFile, folderId)
|| 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<String> iterator = changes.keySet().iterator();
while (iterator.hasNext()) {
String driveId = iterator.next();
File file = changes.get(driveId);
if (!SyncUtils.isInMyTracksAndValid(file, folderId)
&& !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.
*/
private void insertNewDriveFiles() throws IOException {
Cursor cursor = null;
try {
cursor = myTracksProviderUtils.getTrackCursor(SyncUtils.NO_DRIVE_ID_TRACKS_QUERY, null, null);
long recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
if (cursor != null && cursor.moveToFirst()) {
do {
Track track = myTracksProviderUtils.createTrack(cursor);
if (track.getId() == recordingTrackId) {
continue;
}
// If not successful, the next sync will retry again
SyncUtils.insertDriveFile(
drive, folderId, context, myTracksProviderUtils, track, true, true);
} while (cursor.moveToNext());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
/**
* Inserts new tracks from a collection of drive files.
*
* @param driveFiles the drive files
*/
private void insertNewTracks(Collection<File> driveFiles) throws IOException {
for (File driveFile : driveFiles) {
if (driveFile == null) {
return;
}
updateTrack(-1L, driveFile);
}
}
/**
* Gets all the files from a request.
*
* @param request the request
* @param excludeSharedWithMe true to exclude shared with me files
* @return a map of file id to file
*/
private Map<String, File> getFiles(Files.List request, boolean excludeSharedWithMe)
throws IOException {
Map<String, File> idToFileMap = new HashMap<String, File>();
do {
FileList files = request.execute();
for (File file : files.getItems()) {
if (excludeSharedWithMe && file.getSharedWithMeDate() != null) {
continue;
}
idToFileMap.put(file.getId(), file);
}
request.setPageToken(files.getNextPageToken());
} while (request.getPageToken() != null && request.getPageToken().length() > 0);
return idToFileMap;
}
/**
* Gets the drive changes info in the My Tracks folder, including deleted
* files.
*
* @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(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 (file.getLabels().getTrashed()) {
changes.put(change.getFileId(), null);
} else {
changes.put(change.getFileId(), file);
}
}
}
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;
}
/**
* Merges a track with a drive file.
*
* @param track the track
* @param driveFile the drive file
*/
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 for track " + track.getName() + " and drive file "
+ driveFile.getTitle());
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 for track " + track.getName() + " and drive file "
+ driveFile.getTitle());
if (!updateTrack(track.getId(), driveFile)) {
Log.e(TAG, "Unable to update drive change");
// The track could have been deleted in the unsuccessful update
track = myTracksProviderUtils.getTrack(track.getId());
if (track != null) {
track.setModifiedTime(driveModifiedTime);
myTracksProviderUtils.updateTrack(track);
}
}
}
}
/**
* Updates a track based on a drive file. Returns true if successful.
*
* @param trackId the track id. -1L to insert a new track
* @param driveFile the drive file
*/
private boolean updateTrack(final long trackId, File driveFile) throws IOException {
Track track = null;
boolean success = false;
try {
track = importDriveFile(trackId, driveFile);
if (track == null) {
return false;
}
File updatedDriveFile;
String trackName = FileUtils.getName(driveFile.getTitle());
if (SyncUtils.isInMyTracks(driveFile, folderId) && !track.getName().equals(trackName)) {
track.setName(trackName);
/*
* The drive file title and the track name inside the drive file do not
* match, update the drive file.
*/
java.io.File file = null;
try {
file = SyncUtils.getTempFile(context, myTracksProviderUtils, track, true);
updatedDriveFile = SyncUtils.updateDriveFile(
drive, driveFile, trackName + "." + KmzTrackExporter.KMZ_EXTENSION, file, true);
if (updatedDriveFile == null) {
Log.e(TAG, "Unable to update drive file");
return false;
}
} finally {
if (file != null) {
file.delete();
}
}
} else {
updatedDriveFile = driveFile;
}
SyncUtils.updateTrack(myTracksProviderUtils, track, updatedDriveFile);
success = true;
return true;
} finally {
if (!success) {
// if the track is new, delete it
if (trackId == -1L && track != null) {
myTracksProviderUtils.deleteTrack(context, track.getId());
}
}
}
}
/**
* Imports drive file to track.
*
* @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 {
InputStream inputStream = null;
try {
inputStream = downloadDriveFile(driveFile, true);
if (inputStream == null) {
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);
}
long importedId = trackImporter.importFile(inputStream);
if (importedId == -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 import drive file. Imported track is null.");
return null;
} else {
return track;
}
} catch (IOException e) {
Log.e(TAG, "Unable to import drive file.", e);
return null;
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
/**
* Deletes a drive file.
*
* @param driveId the drive id
* @param canRetry true if can retry the request
* @throws IOException
*/
private void deleteDriveFile(String driveId, boolean canRetry) throws IOException {
try {
File driveFile = drive.files().get(driveId).execute();
if (SyncUtils.isInMyTracks(driveFile, folderId)) {
if (!driveFile.getLabels().getTrashed()) {
drive.files().trash(driveId).execute();
}
// if trashed, ignore
} else if (SyncUtils.isInSharedWithMe(driveFile)) {
if (!driveFile.getLabels().getTrashed()) {
drive.files().delete(driveId).execute();
}
// if trashed, ignore
}
} catch (UserRecoverableAuthIOException e) {
throw e;
} catch (IOException e) {
if (canRetry) {
deleteDriveFile(driveId, false);
return;
}
Log.e(TAG, "Unable to delete Drive file for " + driveId, e);
}
}
/**
* Downloads a drive file.
*
* @param driveFile the drive file
*/
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;
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright 2013 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.sync;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
/**
* Service to sync with Google Drive.
*
* @author Jimmy Shih
*/
public class SyncService extends Service {
private static final Object lock = new Object();
private static SyncAdapter syncAdapter = null;
@Override
public void onCreate() {
synchronized (lock) {
if (syncAdapter == null) {
syncAdapter = new SyncAdapter(getApplicationContext());
}
}
}
@Override
public IBinder onBind(Intent intent) {
return syncAdapter.getSyncAdapterBinder();
}
}
@@ -1,557 +0,0 @@
/*
* Copyright 2013 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.sync;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.io.file.TrackFileFormat;
import com.google.android.apps.mytracks.io.file.exporter.FileTrackExporter;
import com.google.android.apps.mytracks.io.file.exporter.KmzTrackExporter;
import com.google.android.apps.mytracks.io.file.exporter.TrackExporter;
import com.google.android.apps.mytracks.util.FileUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import com.google.api.client.extensions.android.http.AndroidHttp;
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.FileContent;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Files.List;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.ParentReference;
import com.google.common.annotations.VisibleForTesting;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
/**
* Utilities for Google Drive sync.
*
* @author Jimmy Shih
*/
public class SyncUtils {
// Get tracks with drive id
public static final String DRIVE_ID_TRACKS_QUERY = TracksColumns.DRIVEID + " IS NOT NULL AND "
+ TracksColumns.DRIVEID + "!=''";
// Get tracks without drive id
public static final String NO_DRIVE_ID_TRACKS_QUERY = TracksColumns.DRIVEID + " IS NULL OR "
+ TracksColumns.DRIVEID + "=''";
// KML mime type
public static final String KML_MIME_TYPE = "application/vnd.google-earth.kml+xml";
// KMZ mime type
public static final String KMZ_MIME_TYPE = "application/vnd.google-earth.kmz";
// KML and KMZ mime types
private static final String KML_KMZ_MINE_TYPES = "not (mimeType != '" + KML_MIME_TYPE
+ "' and mimeType != '" + KMZ_MIME_TYPE + "')";
// Get KML/KMZ files in the My Tracks folder
public static final String MY_TRACKS_FOLDER_FILES_QUERY = "'%s' in parents and "
+ KML_KMZ_MINE_TYPES + " and trashed = false";
// Get shared with me KML/KMZ files
public static final String SHARED_WITH_ME_FILES_QUERY = "sharedWithMe and " + KML_KMZ_MINE_TYPES
+ " and trashed = false";
// Folder mime type
private static final String FOLDER_MIME_TYPE = "application/vnd.google-apps.folder";
// Get My Tracks folder
@VisibleForTesting
public static final String MY_TRACKS_FOLDER_QUERY =
"'root' in parents and title = '%s' and mimeType = '" + FOLDER_MIME_TYPE
+ "' and trashed = false";
private static final String TAG = SyncUtils.class.getSimpleName();
private static final String SYNC_AUTHORITY = "com.google.android.maps.mytracks";
private SyncUtils() {}
/**
* Syncs now for the current account.
*
* @param context the context
*/
public static void syncNow(Context context) {
Account[] accounts = AccountManager.get(context).getAccountsByType(Constants.ACCOUNT_TYPE);
String googleAccount = PreferencesUtils.getString(
context, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
for (Account account : accounts) {
if (account.name.equals(googleAccount)) {
ContentResolver.cancelSync(account, SYNC_AUTHORITY);
ContentResolver.requestSync(account, SYNC_AUTHORITY, new Bundle());
break;
}
}
}
/**
* Disables sync.
*
* @param context the context
*/
public static void disableSync(Context context) {
// Set preference
PreferencesUtils.setBoolean(
context, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
// Disable sync for all accounts
disableSyncForAll(context);
// Clear sync state
clearSyncState(context);
}
/**
* Disables sync for all accounts.
*
* @param context the context
*/
private static void disableSyncForAll(Context context) {
Account[] accounts = AccountManager.get(context).getAccountsByType(Constants.ACCOUNT_TYPE);
for (Account account : accounts) {
ContentResolver.cancelSync(account, SYNC_AUTHORITY);
ContentResolver.setIsSyncable(account, SYNC_AUTHORITY, 0);
ContentResolver.setSyncAutomatically(account, SYNC_AUTHORITY, false);
}
}
/**
* Returns true if sync is active.
*
* @param context the context
*/
public static boolean isSyncActive(Context context) {
Account[] accounts = AccountManager.get(context).getAccountsByType(Constants.ACCOUNT_TYPE);
for (Account account : accounts) {
if (ContentResolver.isSyncActive(account, SYNC_AUTHORITY)) {
return true;
}
}
return false;
}
/**
* Enables sync.
*
* @param context the context
*/
public static void enableSync(Context context) {
// Set preference
PreferencesUtils.setBoolean(context, R.string.drive_sync_key, true);
// Disable sync for all accounts
disableSyncForAll(context);
// Turn on sync
ContentResolver.setMasterSyncAutomatically(true);
// Enable sync for account
String googleAccount = PreferencesUtils.getString(
context, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
enableSyncForAccount(new Account(googleAccount, Constants.ACCOUNT_TYPE));
}
/**
* Enables sync for an account.
*
* @param account the account
*/
private static void enableSyncForAccount(Account account) {
ContentResolver.setIsSyncable(account, SYNC_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, SYNC_AUTHORITY, true);
ContentResolver.requestSync(account, SYNC_AUTHORITY, new Bundle());
}
/**
* Clears the sync state. Assumes sync is turned off. Do not want clearing the
* sync state to cause sync activities.
*/
private static void clearSyncState(Context context) {
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
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);
if (track.isSharedWithMe()) {
myTracksProviderUtils.deleteTrack(context, track.getId());
} else {
SyncUtils.updateTrack(myTracksProviderUtils, track, null);
}
} while (cursor.moveToNext());
}
} finally {
if (cursor != null) {
cursor.close();
}
}
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);
}
/**
* Gets the drive service.
*
* @param credential the credential
*/
public static Drive getDriveService(GoogleAccountCredential credential) {
return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
.build();
}
/**
* Gets the My Tracks folder. Creates one if necessary.
*
* @param context the context
* @param drive the drive
*/
public static File getMyTracksFolder(Context context, Drive drive) throws IOException {
String folderName = context.getString(R.string.my_tracks_app_name);
List list = drive.files()
.list().setQ(String.format(Locale.US, MY_TRACKS_FOLDER_QUERY, folderName));
FileList result = list.execute();
for (File file : result.getItems()) {
if (file.getSharedWithMeDate() == null) {
return file;
}
}
File file = new File();
file.setTitle(folderName);
file.setMimeType(FOLDER_MIME_TYPE);
return drive.files().insert(file).execute();
}
/**
* 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 isInMyTracks(File driveFile, String folderId) {
if (driveFile == null) {
return false;
}
String mimeType = driveFile.getMimeType();
if (!SyncUtils.KML_MIME_TYPE.equals(mimeType) && !SyncUtils.KMZ_MIME_TYPE.equals(mimeType)) {
return false;
}
if (driveFile.getSharedWithMeDate() != null) {
return false;
}
for (ParentReference parentReference : driveFile.getParents()) {
String id = parentReference.getId();
if (id != null && id.equals(folderId)) {
return true;
}
}
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.
*
* @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
* @param updateTrack true to update track
* @return the added drive file or null.
*/
public static File insertDriveFile(Drive drive, String folderId, Context context,
MyTracksProviderUtils myTracksProviderUtils, Track track, boolean canRetry, boolean updateTrack)
throws IOException {
java.io.File file = null;
try {
file = getTempFile(context, myTracksProviderUtils, track, true);
if (file == null) {
Log.e(TAG, "Unable to add Drive file. File is null for track " + track.getName());
return null;
}
Log.d(TAG, "Add Drive file for track " + track.getName());
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;
}
if (updateTrack) {
SyncUtils.updateTrack(myTracksProviderUtils, track, uploadedFile);
}
return uploadedFile;
} finally {
if (file != null) {
file.delete();
}
}
}
/**
* Inserts a drive file using info from a track file.
*
* @param drive the drive
* @param folderId the folder id
* @param trackName the track name
* @param file the track file
* @param canRetry true if can retry
*/
private static File insertDriveFile(
Drive drive, String folderId, String trackName, java.io.File file, boolean canRetry)
throws IOException {
try {
// 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(trackName + "." + KmzTrackExporter.KMZ_EXTENSION);
newMetaData.setMimeType(KMZ_MIME_TYPE);
newMetaData.setParents(parents);
FileContent fileContent = new FileContent(KMZ_MIME_TYPE, file);
return drive.files().insert(newMetaData, fileContent).execute();
} catch (UserRecoverableAuthIOException e) {
throw e;
} catch (IOException e) {
if (canRetry) {
return insertDriveFile(drive, folderId, trackName, file, false);
}
throw e;
}
}
/**
* Updates a drive file using info from a track. Returns true if successful.
*
* @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(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 = null;
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 = 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;
}
long modifiedTime = updatedFile.getModifiedDate().getValue();
if (track.getModifiedTime() != modifiedTime) {
track.setModifiedTime(modifiedTime);
myTracksProviderUtils.updateTrack(track);
}
return true;
} finally {
if (file != null) {
file.delete();
}
}
}
/**
* Updates a drive file using a track file.
*
* @param drive the drive
* @param driveFile the drive file
* @param driveTitle the drive title
* @param file the track file. If null, just update the driveFile meta data
* @param canRetry true if can retry
*/
public static File updateDriveFile(
Drive drive, File driveFile, String driveTitle, java.io.File file, boolean canRetry)
throws IOException {
try {
driveFile.setTitle(driveTitle);
driveFile.setMimeType(KMZ_MIME_TYPE);
if (file != null) {
FileContent fileContent = new FileContent(KMZ_MIME_TYPE, file);
return drive.files().update(driveFile.getId(), driveFile, fileContent).execute();
} else {
return drive.files().update(driveFile.getId(), driveFile).execute();
}
} catch (UserRecoverableAuthIOException e) {
throw e;
} catch (IOException e) {
if (canRetry) {
return updateDriveFile(drive, driveFile, driveTitle, file, false);
}
throw e;
}
}
/**
* Gets a temporary file for a track.
*
* @param context the context
* @param myTracksProviderUtils the myMyTracksProviderUtils
* @param track the track
* @param useKmz true to output kmz
*/
public static java.io.File getTempFile(
Context context, MyTracksProviderUtils myTracksProviderUtils, Track track, boolean useKmz)
throws FileNotFoundException {
java.io.File directory = new java.io.File(context.getCacheDir(), FileUtils.TEMP_FILES_DIR);
if (!FileUtils.ensureDirectoryExists(directory)) {
Log.d(TAG, "Unable to create " + directory.getAbsolutePath());
return null;
}
for (java.io.File file : directory.listFiles()) {
file.delete();
}
Track[] tracks = new Track[] { track };
String extension = useKmz ? KmzTrackExporter.KMZ_EXTENSION : TrackFileFormat.KML.getExtension();
java.io.File file = new java.io.File(
directory, FileUtils.buildUniqueFileName(directory, track.getName(), extension));
FileTrackExporter fileTrackExporter = new FileTrackExporter(myTracksProviderUtils, tracks,
TrackFileFormat.KML.newTrackWriter(context, false, false), null);
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(
myTracksProviderUtils, fileTrackExporter, tracks, context)
: fileTrackExporter;
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
if (trackExporter.writeTrack(fileOutputStream)) {
return file;
} else {
if (!file.delete()) {
Log.d(TAG, "Unable to delete file for track " + track.getName());
}
Log.d(TAG, "Unable to get file for track " + track.getName());
return null;
}
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
Log.e(TAG, "Unable to close file output stream", e);
}
}
}
}
/**
* Updates a track with info from a drive file.
*
* @param myTracksProviderUtils the myTracksProviderUtils
* @param track the track
* @param driveFile the drive file
*/
public static void updateTrack(
MyTracksProviderUtils myTracksProviderUtils, Track track, File driveFile) {
track.setDriveId(driveFile != null ? driveFile.getId() : "");
track.setModifiedTime(driveFile != null ? driveFile.getModifiedDate().getValue() : -1L);
track.setSharedWithMe(driveFile != null ? driveFile.getSharedWithMeDate() != null : false);
track.setSharedOwner(driveFile != null && driveFile.getSharedWithMeDate() != null
&& driveFile.getOwnerNames().size() > 0 ? driveFile.getOwnerNames().get(0)
: "");
myTracksProviderUtils.updateTrack(track);
}
}
@@ -16,16 +16,16 @@
package com.google.android.apps.mytracks.maps;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils;
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.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
/**
* A dynamic speed path descriptor.
@@ -15,15 +15,15 @@
*/
package com.google.android.apps.mytracks.maps;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.MapOverlay.CachedLocation;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import java.util.ArrayList;
import java.util.List;
@@ -16,18 +16,18 @@
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.app.IntentService;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.google.android.apps.mytracks.widgets.TrackWidgetProvider;
import com.google.android.maps.mytracks.R;
/**
* A service to control starting and stopping of a recording. This service,
* through the AndroidManifest.xml, is configured to only allow components of
@@ -27,12 +27,11 @@ import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager.WakeLock;
import android.os.Process;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
@@ -67,15 +66,10 @@ import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.apps.mytracks.util.TrackIconUtils;
import com.google.android.apps.mytracks.util.TrackNameUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.ActivityRecognitionClient;
import com.google.android.gms.location.DetectedActivity;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationResult;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -16,22 +16,22 @@
package com.google.android.apps.mytracks.services.tasks;
import android.content.Context;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import android.support.annotation.VisibleForTesting;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.google.android.apps.mytracks.services.TrackRecordingService;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.apps.mytracks.util.UnitConversions;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.media.AudioManager;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.util.HashMap;
import java.util.Locale;
@@ -1,99 +0,0 @@
/*
* Copyright 2013 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.services.tasks;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
/**
* An async task to check permission.
*
* @author Jimmy Shih
*/
public class CheckPermissionAsyncTask extends AsyncTask<Void, Void, Boolean> {
public interface CheckPermissionCaller {
public void onCheckPermissionDone(String scope, boolean success, Intent userRecoverableIntent);
}
private static final String TAG = CheckPermissionAsyncTask.class.getSimpleName();
private Activity activity;
private final String accountName;
private final String scope;
/**
* True if the AsyncTask result is success.
*/
private boolean success;
/**
* User recoverable intent if failed.
*/
private Intent userRecoverableIntent;
/**
* True if the AsyncTask has completed.
*/
private boolean completed;
/**
* True if can retry the AsyncTask.
*/
private boolean canRetry;
public CheckPermissionAsyncTask(Activity activity, String accountName, String scope) {
this.activity = activity;
this.accountName = accountName;
this.scope = scope;
success = false;
userRecoverableIntent = null;
completed = false;
canRetry = true;
}
public void setActivity(Activity activity) {
this.activity = activity;
if (completed && activity != null) {
((CheckPermissionCaller) activity).onCheckPermissionDone(
scope, success, userRecoverableIntent);
}
}
@Override
protected Boolean doInBackground(Void... params) {
return null;
}
@Override
protected void onPostExecute(Boolean result) {
success = result;
completed = true;
if (activity != null) {
((CheckPermissionCaller) activity).onCheckPermissionDone(
scope, success, userRecoverableIntent);
}
}
}
@@ -17,7 +17,6 @@
package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
import com.google.android.apps.mytracks.util.DialogUtils;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
@@ -198,8 +197,6 @@ public class AdvancedSettingsActivity extends AbstractSettingsActivity {
// Actually wipe preferences and save synchronously
sharedPreferences.edit().clear().commit();
SyncUtils.disableSync(AdvancedSettingsActivity.this);
// Give UI feedback in the UI thread
runOnUiThread(new Runnable() {
@Override
@@ -16,88 +16,33 @@
package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
import com.google.android.apps.mytracks.services.tasks.CheckPermissionAsyncTask;
import com.google.android.apps.mytracks.services.tasks.CheckPermissionAsyncTask.CheckPermissionCaller;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
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.maps.mytracks.R;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.widget.Toast;
/**
* An activity for accessing settings.
*
* @author Jimmy Shih
*/
public class SettingsActivity extends AbstractSettingsActivity implements CheckPermissionCaller {
private static final int DIALOG_NO_ACCOUNT = 0;
private static final int DIALOG_CHOOSE_ACCOUNT = 1;
private static final int DIALOG_CONFIRM_DRIVE_SYNC_ON = 2;
private static final int DRIVE_REQUEST_CODE = 0;
private CheckBoxPreference syncDrivePreference;
private CheckPermissionAsyncTask syncDriveAsyncTask;
public class SettingsActivity extends AbstractSettingsActivity {
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof CheckPermissionAsyncTask) {
syncDriveAsyncTask = (CheckPermissionAsyncTask) retained;
syncDriveAsyncTask.setActivity(this);
}
addPreferencesFromResource(R.xml.settings);
syncDrivePreference = (CheckBoxPreference) findPreference(getString(R.string.drive_sync_key));
syncDrivePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if ((Boolean) newValue) {
Account[] accounts = AccountManager.get(SettingsActivity.this)
.getAccountsByType(Constants.ACCOUNT_TYPE);
if (accounts.length == 0) {
PreferencesUtils.setString(SettingsActivity.this, R.string.google_account_key,
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
handleSync(false);
showDialog(DIALOG_NO_ACCOUNT);
} else if (accounts.length == 1) {
PreferencesUtils.setString(
SettingsActivity.this, R.string.google_account_key, accounts[0].name);
showDialog(DIALOG_CONFIRM_DRIVE_SYNC_ON);
} else {
showDialog(DIALOG_CHOOSE_ACCOUNT);
}
} else {
PreferencesUtils.setString(SettingsActivity.this, R.string.google_account_key,
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
handleSync(false);
}
return false;
}
});
updateSyncDriveSummary();
configPreference(R.string.settings_map_key, MapSettingsActivity.class);
configPreference(R.string.settings_chart_key, ChartSettingsActivity.class);
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
@@ -106,100 +51,6 @@ public class SettingsActivity extends AbstractSettingsActivity implements CheckP
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
}
@Deprecated
public Object onRetainNonConfigurationInstance() {
if (syncDriveAsyncTask != null) {
syncDriveAsyncTask.setActivity(null);
}
return syncDriveAsyncTask;
}
@Override
protected Dialog onCreateDialog(int id, Bundle bundle) {
final Dialog dialog;
switch (id) {
case DIALOG_NO_ACCOUNT:
dialog = new AlertDialog.Builder(this).setMessage(R.string.send_google_no_account_message)
.setTitle(R.string.send_google_no_account_title)
.setPositiveButton(R.string.generic_ok, null).create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialogInterface) {
DialogUtils.setDialogTitleDivider(SettingsActivity.this, dialog);
}
});
break;
default:
dialog = null;
}
return dialog;
}
@SuppressWarnings("deprecation")
@Override
protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
if (id == DIALOG_CONFIRM_DRIVE_SYNC_ON) {
AlertDialog alertDialog = (AlertDialog) dialog;
String googleAccount = PreferencesUtils.getString(
this, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
alertDialog.setMessage(getString(R.string.sync_drive_confirm_message, googleAccount,
getString(R.string.my_tracks_app_name)));
}
super.onPrepareDialog(id, dialog, bundle);
}
@Override
public void onCheckPermissionDone(String scope, boolean success, Intent userRecoverableIntent) {
syncDriveAsyncTask = null;
if (success) {
onDrivePermissionSuccess();
} else {
if (userRecoverableIntent != null) {
startActivityForResult(userRecoverableIntent, DRIVE_REQUEST_CODE);
} else {
onDrivePermissionFailure();
}
}
}
private void onDrivePermissionSuccess() {
handleSync(true);
}
private void onDrivePermissionFailure() {
Toast.makeText(
SettingsActivity.this, R.string.send_google_no_account_permission, Toast.LENGTH_LONG)
.show();
}
/**
* Handles sync.
*
* @param value true to sync
*/
private void handleSync(boolean value) {
syncDrivePreference.setChecked(value);
updateSyncDriveSummary();
if (value) {
SyncUtils.enableSync(this);
} else {
SyncUtils.disableSync(this);
}
}
/**
* Updates UI by account.
*/
private void updateSyncDriveSummary() {
String googleAccount = PreferencesUtils.getString(
this, R.string.google_account_key, PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
boolean hasAccount = !PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT.equals(googleAccount);
syncDrivePreference.setSummaryOn(
hasAccount ? getString(R.string.settings_google_drive_sync_summary_on, googleAccount) : "");
}
/**
* Configures a preference by starting a new activity when it is clicked.
*
@@ -16,16 +16,16 @@
package com.google.android.apps.mytracks.stats;
import static com.google.android.apps.mytracks.services.TrackRecordingService.MAX_NO_MOVEMENT_SPEED;
import static com.google.android.apps.mytracks.services.TrackRecordingService.PAUSE_LATITUDE;
import android.location.Location;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.google.android.apps.mytracks.util.CalorieUtils;
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.common.annotations.VisibleForTesting;
import android.location.Location;
import android.util.Log;
import static com.google.android.apps.mytracks.services.TrackRecordingService.MAX_NO_MOVEMENT_SPEED;
import static com.google.android.apps.mytracks.services.TrackRecordingService.PAUSE_LATITUDE;
/**
* Updater for {@link TripStatistics}. For updating track trip statistics as new
@@ -1,75 +0,0 @@
/*
* Copyright 2013 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.util;
import com.google.android.maps.mytracks.R;
import android.accounts.Account;
import android.content.Context;
import android.support.v4.app.FragmentActivity;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
/**
* Utilities for account.
*
* @author Jimmy Shih
*/
public class AccountUtils {
private AccountUtils() {}
/**
* Sets up account spinner.
*
* @param fragmentActivity tyhe fragment activity
* @param spinner the spinner
* @param accounts the accounts
*/
public static void setupAccountSpinner(
FragmentActivity fragmentActivity, Spinner spinner, Account[] accounts) {
if (accounts.length > 1) {
String shareTrackAccount = PreferencesUtils.getString(fragmentActivity,
R.string.share_track_account_key, PreferencesUtils.SHARE_TRACK_ACCOUNT_DEFAULT);
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
fragmentActivity, R.layout.account_spinner_item);
int selection = 0;
for (int i = 0; i < accounts.length; i++) {
String name = accounts[i].name;
adapter.add(name);
if (name.equals(shareTrackAccount)) {
selection = i;
}
}
adapter.setDropDownViewResource(R.layout.account_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setSelection(selection);
}
}
/**
* Updates the share track account preference.
*
* @param context the context
* @param account the account
*/
public static void updateShareTrackAccountPreference(Context context, Account account) {
if (account != null) {
PreferencesUtils.setString(context, R.string.share_track_account_key, account.name);
}
}
}
@@ -16,12 +16,12 @@
package com.google.android.apps.mytracks.util;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import java.util.Vector;
@@ -1,88 +0,0 @@
/*
* Copyright 2008 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.util;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
/**
* Utilities for EULA.
*
* @author Jimmy Shih
*/
public class EulaUtils {
private static final String EULA_PREFERENCE_FILE = "eula";
// Accepting Google mobile terms of service
private static final String ACCEPTED_EULA_PREFERENCE_KEY = "eula.google_mobile_tos_accepted";
private static final String SHOW_ENABLE_SYNC_PREFERENCE_KEY = "showEnableSync";
private EulaUtils() {}
/**
* Returns true if has accepted eula.
*
* @param context the context
*/
public static boolean hasAcceptedEula(Context context) {
return getValue(context, ACCEPTED_EULA_PREFERENCE_KEY, false);
}
/**
* Sets to true accepted eula.
*
* @param context the context
*/
public static void setAcceptedEula(Context context) {
setValue(context, ACCEPTED_EULA_PREFERENCE_KEY, true);
}
/**
* Returns true if should show enable sync.
*
* @param context the context
*/
public static boolean showEnableSync(Context context) {
return getValue(context, SHOW_ENABLE_SYNC_PREFERENCE_KEY, true);
}
/**
* Sets to false show enable sync.
*
* @param context the context
*/
public static void setShowEnableSync(Context context) {
setValue(context, SHOW_ENABLE_SYNC_PREFERENCE_KEY, false);
}
private static boolean getValue(Context context, String key, boolean defaultValue) {
SharedPreferences sharedPreferences = context.getSharedPreferences(
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
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);
Editor editor = sharedPreferences.edit();
editor.putBoolean(key, value);
ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
}
@@ -1,95 +0,0 @@
/*
* Copyright 2013 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.util;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.io.sync.SyncUtils;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.util.Log;
import java.util.List;
/**
* Utilities for Google Earth.
*
* @author Jimmy Shih
*/
public class GoogleEarthUtils {
private static final String TAG = GoogleEarthUtils.class.getSimpleName();
private static final String GOOGLE_EARTH_CLASS = "com.google.earth.EarthActivity";
private static final int GOOGLE_EARTH_MIN_VERSION_CODE = 13246120;
private static final String GOOGLE_EARTH_PACKAGE = "com.google.earth";
private static final String
GOOGLE_EARTH_TOUR_FEATURE_ID = "com.google.earth.EXTRA.tour_feature_id";
public static final String GOOGLE_EARTH_MARKET_URL = "market://details?id="
+ GOOGLE_EARTH_PACKAGE;
public static final String TOUR_FEATURE_ID_VALUE = "tour";
private GoogleEarthUtils() {}
/**
* Returns true if Google Earth is installed.
*
* @param context the context
*/
public static boolean isEarthInstalled(Context context) {
List<ResolveInfo> infos = context.getPackageManager().queryIntentActivities(
new Intent().setType(SyncUtils.KMZ_MIME_TYPE), PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info : infos) {
if (info.activityInfo != null && info.activityInfo.packageName != null
&& info.activityInfo.packageName.equals(GOOGLE_EARTH_PACKAGE)) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(info.activityInfo.packageName, 0);
return packageInfo.versionCode >= GOOGLE_EARTH_MIN_VERSION_CODE;
} catch (NameNotFoundException e) {
Log.e(TAG, "Unable to get google earth package info", e);
return false;
}
}
}
return false;
}
/**
* Gets an intent to play a kml file in Google Earth.
*
* @param kmlFilePath the kml file path
*/
public static Intent getPlayInEarthIntent(Context context, String kmlFilePath) {
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(MyTracksProviderUtils.AUTHORITY).path(kmlFilePath).build();
context.grantUriPermission(GOOGLE_EARTH_PACKAGE, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
return new Intent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(GOOGLE_EARTH_TOUR_FEATURE_ID, TOUR_FEATURE_ID_VALUE)
.setClassName(GOOGLE_EARTH_PACKAGE, GOOGLE_EARTH_CLASS)
.setDataAndType(uri, SyncUtils.KMZ_MIME_TYPE);
}
}
@@ -1,63 +0,0 @@
/*
* Copyright 2013 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.util;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
/**
* Utilities for Google Feedback.
*
* @author Jimmy Shih
*/
public class GoogleFeedbackUtils {
private static final String TAG = GoogleFeedbackUtils.class.getSimpleName();
private GoogleFeedbackUtils() {}
/**
* Binds the Google Feedback service.
*
* @param context the context
*/
public static void bindFeedback(Context context) {
Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
service.transact(Binder.FIRST_CALL_TRANSACTION, Parcel.obtain(), null, 0);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
// Bind to the service after creating it if necessary
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
@@ -61,20 +61,11 @@ 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;
public static final String EXPORT_EXTERNAL_STORAGE_FORMAT_DEFAULT = TrackFileFormat.KML.name();
public static final boolean EXPORT_GOOGLE_FUSION_TABLES_PUBLIC_DEFAULT = false;
public static final boolean EXPORT_GOOGLE_MAPS_PUBLIC_DEFAULT = false;
public static final String EXPORT_TYPE_DEFAULT = ExportType.GOOGLE_DRIVE.name();
// Value for split_frequency_key and voice_frequency_key
public static final int FREQUENCY_OFF = 0;
public static final String GOOGLE_ACCOUNT_DEFAULT = "";
public static final int MAP_TYPE_DEFAUlT = 1;
public static final int MAX_RECORDING_DISTANCE_DEFAULT = 200;
@@ -93,14 +84,8 @@ public class PreferencesUtils {
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
public static final boolean RECORDING_TRACK_PAUSED_DEFAULT = true;
public static final long SELECTED_TRACK_ID_DEFAULT = -1L;
public static final String SENSOR_TYPE_DEFAULT = "NONE";
// Share track
public static final String SHARE_TRACK_ACCOUNT_DEFAULT = "";
public static final boolean SHARE_TRACK_INVITE_DEFAULT = false;
public static final boolean SHARE_TRACK_PUBLIC_DEFAULT = false;
public static final int SPLIT_FREQUENCY_DEFAULT = 0;
// Stats
@@ -16,13 +16,13 @@
package com.google.android.apps.mytracks.util;
import com.google.android.maps.mytracks.R;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.support.annotation.VisibleForTesting;
import com.google.android.maps.mytracks.R;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -63,12 +63,5 @@ public class TrackUtils {
CalorieUtils.updateTrackCalorie(context, track);
}
}
boolean driveSync = PreferencesUtils.getBoolean(
context, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
if (driveSync) {
PreferencesUtils.addToList(context, R.string.drive_edited_list_key,
PreferencesUtils.DRIVE_EDITED_LIST_DEFAULT, String.valueOf(track.getId()));
}
}
}