Merge heads

This commit is contained in:
youtaol
2013-04-17 19:51:58 +08:00
parent 3d6f9a1a2d
commit d66bf7b395
93 changed files with 840 additions and 734 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto"
android:versionCode="65"
android:versionCode="66"
android:versionName="2.0.4"
package="com.google.android.maps.mytracks" >
<!-- Permission to initialize services -->
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -18,6 +18,8 @@ package com.google.android.apps.mytracks;
import com.google.android.apps.mytracks.fragments.AddEmailsDialogFragment;
import com.google.android.apps.mytracks.fragments.AddEmailsDialogFragment.AddEmailsCaller;
import com.google.android.apps.mytracks.fragments.CheckPermissionFragment;
import com.google.android.apps.mytracks.fragments.CheckPermissionFragment.CheckPermissionCaller;
import com.google.android.apps.mytracks.fragments.ChooseAccountDialogFragment;
import com.google.android.apps.mytracks.fragments.ChooseAccountDialogFragment.ChooseAccountCaller;
import com.google.android.apps.mytracks.fragments.ChooseActivityDialogFragment;
@@ -27,7 +29,6 @@ import com.google.android.apps.mytracks.io.fusiontables.SendFusionTablesActivity
import com.google.android.apps.mytracks.io.gdata.maps.MapsConstants;
import com.google.android.apps.mytracks.io.maps.ChooseMapActivity;
import com.google.android.apps.mytracks.io.maps.SendMapsActivity;
import com.google.android.apps.mytracks.io.sendtogoogle.PermissionCallback;
import com.google.android.apps.mytracks.io.sendtogoogle.SendRequest;
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
import com.google.android.apps.mytracks.io.sendtogoogle.UploadResultActivity;
@@ -47,6 +48,7 @@ import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.Toast;
@@ -58,91 +60,58 @@ import java.io.IOException;
* @author Jimmy Shih
*/
public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActivity
implements ChooseAccountCaller, AddEmailsCaller, ChooseActivityCaller {
implements ChooseAccountCaller, CheckPermissionCaller, AddEmailsCaller, ChooseActivityCaller {
private static final String TAG = AbstractMyTracksActivity.class.getSimpleName();
private static final String SEND_REQUEST_KEY = "send_request_key";
private SendRequest sendRequest;
private PermissionCallback driveCallback = new PermissionCallback() {
@Override
public void onSuccess() {
getPermission(MapsConstants.SERVICE_NAME, sendRequest.isSendMaps(), mapsCallback);
}
@Override
public void onFailure() {
handleNoAccountPermission();
}
};
private PermissionCallback mapsCallback = new PermissionCallback() {
@Override
public void onSuccess() {
checkFusionTablesPermission();
}
@Override
public void onFailure() {
handleNoAccountPermission();
}
};
private PermissionCallback fusionTablesCallback = new PermissionCallback() {
@Override
public void onSuccess() {
checkSpreadsheetPermission();
}
@Override
public void onFailure() {
handleNoAccountPermission();
}
};
private PermissionCallback spreadsheetsCallback = new PermissionCallback() {
@Override
public void onSuccess() {
startNextActivity();
}
@Override
public void onFailure() {
handleNoAccountPermission();
}
};
public void sendToGoogle(SendRequest request) {
sendRequest = request;
new ChooseAccountDialogFragment().show(
getSupportFragmentManager(), ChooseAccountDialogFragment.CHOOSE_ACCOUNT_DIALOG_TAG);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
sendRequest = savedInstanceState.getParcelable(SEND_REQUEST_KEY);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(SEND_REQUEST_KEY, sendRequest);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE:
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
if (resultCode == Activity.RESULT_OK) {
driveCallback.onSuccess();
onDrivePermissionSuccess();
} else {
driveCallback.onFailure();
onPermissionFailure();
}
break;
case SendToGoogleUtils.FUSION_TABLES_PERMISSION_REQUEST_CODE:
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.FUSION_TABLES_NOTIFICATION_ID);
if (resultCode == Activity.RESULT_OK) {
fusionTablesCallback.onSuccess();
onFusionTablesSuccess();
} else {
fusionTablesCallback.onFailure();
onPermissionFailure();
}
break;
case SendToGoogleUtils.SPREADSHEET_PERMISSION_REQUEST_CODE:
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.SPREADSHEET_NOTIFICATION_ID);
if (resultCode == Activity.RESULT_OK) {
spreadsheetsCallback.onSuccess();
onSpreadsheetsPermissionSuccess();
} else {
spreadsheetsCallback.onFailure();
onPermissionFailure();
}
break;
default:
@@ -157,7 +126,53 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
return;
}
sendRequest.setAccount(new Account(googleAccount, Constants.ACCOUNT_TYPE));
checkDrivePermission();
// Check Drive permission
boolean needDrivePermission = sendRequest.isSendDrive();
if (!needDrivePermission) {
boolean defaultTablePublic = PreferencesUtils.getBoolean(
this, R.string.default_table_public_key, PreferencesUtils.DEFAULT_TABLE_PUBLIC_DEFAULT);
needDrivePermission = defaultTablePublic && sendRequest.isSendFusionTables();
}
if (!needDrivePermission) {
needDrivePermission = sendRequest.isSendSpreadsheets();
}
if (needDrivePermission) {
Fragment fragment = CheckPermissionFragment.newInstance(
sendRequest.getAccount().name, SendToGoogleUtils.DRIVE_SCOPE);
getSupportFragmentManager()
.beginTransaction().add(fragment, CheckPermissionFragment.CHECK_PERMISSION_TAG).commit();
} else {
onDrivePermissionSuccess();
}
}
@Override
public void onCheckPermissionDone(String scope, boolean success, Intent intent) {
if (success) {
if (scope.equals(SendToGoogleUtils.DRIVE_SCOPE)) {
onDrivePermissionSuccess();
} else if (scope.equals(SendToGoogleUtils.FUSION_TABLES_SCOPE)) {
onFusionTablesSuccess();
} else {
onSpreadsheetsPermissionSuccess();
}
} else {
if (intent != null) {
int requestCode;
if (scope.equals(SendToGoogleUtils.DRIVE_SCOPE)) {
requestCode = SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE;
} else if (scope.equals(SendToGoogleUtils.FUSION_TABLES_SCOPE)) {
requestCode = SendToGoogleUtils.FUSION_TABLES_PERMISSION_REQUEST_CODE;
} else {
requestCode = SendToGoogleUtils.SPREADSHEET_PERMISSION_REQUEST_CODE;
}
startActivityForResult(intent, requestCode);
} else {
onPermissionFailure();
}
}
}
@Override
@@ -182,111 +197,92 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
}
}
/**
* Checks the Drive permission.
*/
private void checkDrivePermission() {
boolean needDrivePermission = sendRequest.isSendDrive();
if (!needDrivePermission) {
boolean defaultTablePublic = PreferencesUtils.getBoolean(
this, R.string.default_table_public_key, PreferencesUtils.DEFAULT_TABLE_PUBLIC_DEFAULT);
needDrivePermission = defaultTablePublic && sendRequest.isSendFusionTables();
}
if (!needDrivePermission) {
needDrivePermission = sendRequest.isSendSpreadsheets();
}
if (needDrivePermission) {
SendToGoogleUtils.checkPermissionByActivity(this, sendRequest.getAccount().name,
SendToGoogleUtils.DRIVE_SCOPE, SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE,
driveCallback);
} else {
driveCallback.onSuccess();
}
}
/**
* Checks the Fusion Tables permission.
*/
private void checkFusionTablesPermission() {
if (sendRequest.isSendFusionTables()) {
SendToGoogleUtils.checkPermissionByActivity(this, sendRequest.getAccount().name,
SendToGoogleUtils.FUSION_TABLES_SCOPE,
SendToGoogleUtils.FUSION_TABLES_PERMISSION_REQUEST_CODE, fusionTablesCallback);
} else {
fusionTablesCallback.onSuccess();
}
}
/**
* Checks the Spreadsheet permission.
*/
private void checkSpreadsheetPermission() {
if (sendRequest.isSendSpreadsheets()) {
SendToGoogleUtils.checkPermissionByActivity(this, sendRequest.getAccount().name,
SendToGoogleUtils.SPREADSHEET_SCOPE,
SendToGoogleUtils.SPREADSHEET_PERMISSION_REQUEST_CODE, spreadsheetsCallback);
} else {
spreadsheetsCallback.onSuccess();
}
}
/**
* Gets the user permission to access a service.
*
* @param authTokenType the auth token type of the service
* @param needPermission true if need the permission
* @param callback callback after getting the permission
*/
private void getPermission(
String authTokenType, boolean needPermission, final PermissionCallback callback) {
if (needPermission) {
AccountManager.get(this).getAuthToken(sendRequest.getAccount(), authTokenType, null, this,
private void onDrivePermissionSuccess() {
// Check Maps permission
if (sendRequest.isSendMaps()) {
AccountManager.get(this).getAuthToken(
sendRequest.getAccount(), MapsConstants.SERVICE_NAME, null, this,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
if (future.getResult().getString(AccountManager.KEY_AUTHTOKEN) != null) {
callback.onSuccess();
runOnUiThread(new Runnable() {
@Override
public void run() {
onMapsPermissionSuccess();
}
});
return;
} else {
Log.d(TAG, "auth token is null");
callback.onFailure();
}
} catch (OperationCanceledException e) {
Log.d(TAG, "Unable to get auth token", e);
callback.onFailure();
} catch (AuthenticatorException e) {
Log.d(TAG, "Unable to get auth token", e);
callback.onFailure();
} catch (IOException e) {
Log.d(TAG, "Unable to get auth token", e);
callback.onFailure();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
onPermissionFailure();
}
});
}
}, null);
} else {
callback.onSuccess();
onMapsPermissionSuccess();
}
}
private void onMapsPermissionSuccess() {
// Check Fusion Tables permission
if (sendRequest.isSendFusionTables()) {
Fragment fragment = CheckPermissionFragment.newInstance(
sendRequest.getAccount().name, SendToGoogleUtils.FUSION_TABLES_SCOPE);
getSupportFragmentManager()
.beginTransaction().add(fragment, CheckPermissionFragment.CHECK_PERMISSION_TAG).commit();
} else {
onFusionTablesSuccess();
}
}
private void onFusionTablesSuccess() {
// Check Spreadsheets permission
if (sendRequest.isSendSpreadsheets()) {
Fragment fragment = CheckPermissionFragment.newInstance(
sendRequest.getAccount().name, SendToGoogleUtils.SPREADSHEET_SCOPE);
getSupportFragmentManager()
.beginTransaction().add(fragment, CheckPermissionFragment.CHECK_PERMISSION_TAG).commit();
} else {
onSpreadsheetsPermissionSuccess();
}
}
/**
* Starts the next activity. If
* On spreadsheets permission success. If
* <p>
* sendDrive -> {@link SendDriveActivity}
* isSendDrive and isDriveEnableSync -> enable sync
* <p>
* sendMaps and newMap -> {@link SendMapsActivity}
* isSendDrive and isDriveShare -> show {@link AddEmailsDialogFragment}
* <p>
* sendMaps and !newMap -> {@link ChooseMapActivity}
* isSendDrive -> start {@link SendDriveActivity}
* <p>
* !sendMaps && sendFusionTables -> {@link SendFusionTablesActivity}
* isSendMaps and isMapShare -> show {@link ChooseActivityDialogFragment}
* <p>
* !sendMaps && !sendFusionTables && sendSpreadsheets ->
* {@link SendSpreadsheetsActivity}
* isSendMaps and isMapsExistingMap -> start {@link ChooseMapActivity}
* <p>
* !sendMaps && !sendFusionTables && !sendSpreadsheets ->
* {@link UploadResultActivity}
* isSendMaps and !isMapsExistingMap -> {@link SendMapsActivity}
* <p>
* isSendFusionTables -> start {@link SendFusionTablesActivity}
* <p>
* isSendSpreadsheets -> start {@link SendSpreadsheetsActivity}
* <p>
* else -> start {@link UploadResultActivity}
*/
private void startNextActivity() {
private void onSpreadsheetsPermissionSuccess() {
Class<?> next;
if (sendRequest.isSendDrive()) {
if (sendRequest.isDriveEnableSync()) {
@@ -299,7 +295,7 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
ContentResolver.setMasterSyncAutomatically(true);
// Enable sync for account
SyncUtils.enableSync(sendRequest.getAccount());
SyncUtils.enableSync(sendRequest.getAccount());
return;
} else if (sendRequest.isDriveShare()) {
AddEmailsDialogFragment.newInstance(sendRequest.getTrackId())
@@ -328,9 +324,9 @@ public abstract class AbstractSendToGoogleActivity extends AbstractMyTracksActiv
}
/**
* Handles when not able to get account permission.
* Call when not able to get permission for a google service.
*/
private void handleNoAccountPermission() {
private void onPermissionFailure() {
Toast.makeText(this, R.string.send_google_no_account_permission, Toast.LENGTH_LONG).show();
}
}
@@ -92,7 +92,6 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
// From intent
private long trackId;
private long markerId;
private Track track;
// Preferences
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
@@ -299,10 +298,12 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
menu.findItem(R.id.track_detail_save_tcx)
.setTitle(getString(R.string.menu_save_format, fileTypes[3]));
menu.findItem(R.id.track_detail_edit).setVisible(!track.isSharedWithMe());
Track track = myTracksProviderUtils.getTrack(trackId);
boolean sharedWithMe = track != null ? track.isSharedWithMe() : true;
menu.findItem(R.id.track_detail_edit).setVisible(!sharedWithMe);
shareDriveMenuItem = menu.findItem(R.id.track_detail_share_drive);
shareDriveMenuItem.setEnabled(!track.isSharedWithMe());
shareDriveMenuItem.setVisible(!track.isSharedWithMe());
shareDriveMenuItem.setEnabled(!sharedWithMe);
shareDriveMenuItem.setVisible(!sharedWithMe);
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
playMenuItem = menu.findItem(R.id.track_detail_play);
@@ -377,7 +378,9 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
return true;
case R.id.track_detail_send_google:
AnalyticsUtils.sendPageViews(this, "/action/send_google");
ChooseUploadServiceDialogFragment.newInstance(track.isSharedWithMe()).show(
Track track = myTracksProviderUtils.getTrack(trackId);
boolean sharedWithMe = track != null ? track.isSharedWithMe() : true;
ChooseUploadServiceDialogFragment.newInstance(sharedWithMe).show(
getSupportFragmentManager(),
ChooseUploadServiceDialogFragment.CHOOSE_UPLOAD_SERVICE_DIALOG_TAG);
return true;
@@ -518,6 +521,7 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
if (markerId != -1L) {
// Use the trackId from the marker
Waypoint waypoint = myTracksProviderUtils.getWaypoint(markerId);
if (waypoint == null) {
finish();
@@ -529,8 +533,16 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
finish();
return;
}
track = myTracksProviderUtils.getTrack(trackId);
Track track = myTracksProviderUtils.getTrack(trackId);
if (track == null) {
// Use the last track if markerId is not set
if (markerId == -1L) {
track = myTracksProviderUtils.getLastTrack();
if (track != null) {
trackId = track.getId();
return;
}
}
finish();
return;
}
@@ -587,7 +599,8 @@ public class TrackDetailActivity extends AbstractSendToGoogleActivity
if (isRecording) {
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
} else {
title = track.getName();
Track track = myTracksProviderUtils.getTrack(trackId);
title = track != null ? track.getName() : "";
}
setTitle(title);
}
@@ -47,7 +47,7 @@ public class TrackEditActivity extends AbstractMyTracksActivity
public static final String EXTRA_NEW_TRACK = "new_track";
private static final String TAG = TrackEditActivity.class.getSimpleName();
private static final String ICON_VALUE = "icon_value";
private static final String ICON_VALUE_KEY = "icon_value_key";
private Long trackId;
private MyTracksProviderUtils myTracksProviderUtils;
@@ -115,7 +115,7 @@ public class TrackEditActivity extends AbstractMyTracksActivity
iconValue = null;
if (bundle != null) {
iconValue = bundle.getString(ICON_VALUE);
iconValue = bundle.getString(ICON_VALUE_KEY);
}
if (iconValue == null) {
iconValue = track.getIcon();
@@ -164,7 +164,7 @@ public class TrackEditActivity extends AbstractMyTracksActivity
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(ICON_VALUE, iconValue);
outState.putString(ICON_VALUE_KEY, iconValue);
}
@Override
@@ -0,0 +1,126 @@
/*
* 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.io.sendtogoogle.SendToGoogleUtils;
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.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import java.io.IOException;
/**
* A non UI fragment to check permission.
*
* @author Jimmy Shih
*/
public class CheckPermissionFragment extends Fragment {
/**
* Interface for caller of this fragment.
*
* @author Jimmy Shih
*/
public interface CheckPermissionCaller {
/**
* Called when check permission is done.
*
* @param scope the permission scope
* @param success true if success
* @param intent if not success, intent to prompt for permission
*/
public void onCheckPermissionDone(String scope, boolean success, Intent intent);
}
public static final String CHECK_PERMISSION_TAG = "checkPermission";
private static final String TAG = CheckPermissionFragment.class.getSimpleName();
private static final String KEY_ACCOUNT_NAME = "accountName";
private static final String KEY_SCOPE = "scope";
private CheckPermissionCaller caller;
public static CheckPermissionFragment newInstance(String accountName, String scope) {
Bundle bundle = new Bundle();
bundle.putString(KEY_ACCOUNT_NAME, accountName);
bundle.putString(KEY_SCOPE, scope);
CheckPermissionFragment checkPermissionFragment = new CheckPermissionFragment();
checkPermissionFragment.setArguments(bundle);
return checkPermissionFragment;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
caller = (CheckPermissionCaller) activity;
} catch (ClassCastException e) {
throw new ClassCastException(
activity.toString() + " must implement " + CheckPermissionCaller.class.getSimpleName());
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Only want one instance
setRetainInstance(true);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String accountName = getArguments().getString(KEY_ACCOUNT_NAME);
String scope = getArguments().getString(KEY_SCOPE);
try {
SendToGoogleUtils.getGoogleAccountCredential(getActivity(), accountName, scope);
finish(scope, true, null);
} catch (UserRecoverableAuthException e) {
finish(scope, false, e.getIntent());
} catch (GoogleAuthException e) {
Log.e(TAG, "GoogleAuthException", e);
finish(scope, false, null);
} catch (UserRecoverableAuthIOException e) {
finish(scope, false, e.getIntent());
} catch (IOException e) {
Log.e(TAG, "IOException", e);
finish(scope, false, null);
}
}
});
thread.start();
}
private void finish(final String scope, final boolean success, final Intent intent) {
getFragmentManager().popBackStack();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
caller.onCheckPermissionDone(scope, success, intent);
}
});
}
}
@@ -95,10 +95,31 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
// Current location
private Location currentLocation;
// True to keep the currentLocation visible
/**
* True to continue keeping the current location visible on the screen.
* <p>
* Set to true when <br>
* 1. user clicks on the my location button <br>
* Set to false when <br>
* 1. showing a marker <br>
* 2. user manually zooms/pans
*/
private boolean keepCurrentLocationVisible;
// True to zoom to currentLocation when it is available
/**
* True to zoom/center the current location when it is available.
* <p>
* Set to true when <br>
* 1. user clicks on the my location button <br>
* 2. first location during a recording <br>
* Set to false when <br>
* 1. showing a marker <br>
* 2. user manually zooms/pans <br>
* 3. after zooming to the current location <br>
* <p>
* The last one is to support the use case of zooming only once. After zoom,
* set it to false. E.g., only zoom to the first location during a recording.
*/
private boolean zoomToCurrentLocation;
private OnLocationChangedListener onLocationChangedListener;
@@ -221,8 +242,8 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
keepCurrentLocationVisible = savedInstanceState.getBoolean(
KEEP_CURRENT_LOCATION_VISIBLE_KEY, false);
zoomToCurrentLocation = savedInstanceState.getBoolean(ZOOM_TO_CURRENT_LOCATION_KEY, false);
currentLocation = (Location) savedInstanceState.getParcelable(CURRENT_LOCATION_KEY);
updateCurrentLocation();
Location location = (Location) savedInstanceState.getParcelable(CURRENT_LOCATION_KEY);
setCurrentLocation(location);
}
}
@@ -236,6 +257,7 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
ApiAdapterFactory.getApiAdapter().invalidMenu(getActivity());
}
resumeTrackDataHub();
updateCurrentLocation();
}
@Override
@@ -348,7 +370,7 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
}
boolean myLocationEnabled = true;
if (state == LocationState.DISABLED) {
currentLocation = null;
setCurrentLocation(null);
myLocationEnabled = false;
}
googleMap.setMyLocationEnabled(myLocationEnabled);
@@ -415,14 +437,11 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
@Override
public void onLocationChanged(final Location location) {
if (isResumed()) {
if (isSelectedTrackRecording() && currentLocation == null && location != null) {
zoomToCurrentLocation = true;
}
currentLocation = location;
setCurrentLocation(location);
updateCurrentLocation();
}
}
@Override
public void onHeadingChanged(double heading) {
// We don't care.
@@ -586,6 +605,19 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
}
}
/**
* Sets the current location.
*
* @param location the location
*/
private void setCurrentLocation(Location location) {
// If recording, zoom to the first location
if (isSelectedTrackRecording() && currentLocation == null && location != null) {
zoomToCurrentLocation = true;
}
currentLocation = location;
}
/**
* Updates the current location and zoom to it if necessary.
*/
@@ -684,12 +716,14 @@ public class MyTracksMapFragment extends SupportMapFragment implements TrackData
*/
private LatLng getDefaultLatLng() {
MyTracksProviderUtils myTracksProviderUtils = MyTracksProviderUtils.Factory.get(getActivity());
Location location = myTracksProviderUtils.getLastValidTrackPoint();
if (location != null) {
return new LatLng(location.getLatitude(), location.getLongitude());
} else {
return new LatLng(DEFAULT_LATITUDE, DEFAULT_LONGITUDE);
Track track = myTracksProviderUtils.getLastTrack();
if (track != null) {
Location location = myTracksProviderUtils.getLastValidTrackPoint(track.getId());
if (location != null) {
return new LatLng(location.getLatitude(), location.getLongitude());
}
}
return new LatLng(DEFAULT_LATITUDE, DEFAULT_LONGITUDE);
}
/**
@@ -1,35 +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.sendtogoogle;
/**
* A callback after getting the permission to access a Google service.
*
* @author Jimmy Shih
*/
public interface PermissionCallback {
/**
* To be invoked when the permission is granted.
*/
public void onSuccess();
/**
* To be invoked when the permission is not granted.
*/
public void onFailure();
}
@@ -20,15 +20,12 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.LocationUtils;
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.services.drive.DriveScopes;
import com.google.api.services.fusiontables.FusiontablesScopes;
import com.google.common.annotations.VisibleForTesting;
import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
@@ -174,55 +171,6 @@ public class SendToGoogleUtils {
return credential.getToken();
}
/**
* Checks permission by an activity. Will start an activity to request
* permission using the request code.
*
* @param activity the activity
* @param accountName the account name
* @param scope the scope
* @param requestCode the request code
* @param permissionCallback the permission callback
*/
public static void checkPermissionByActivity(final Activity activity, final String accountName,
final String scope, final int requestCode, final PermissionCallback permissionCallback) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
getGoogleAccountCredential(activity, accountName, scope);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
permissionCallback.onSuccess();
}
});
} catch (UserRecoverableAuthException e) {
activity.startActivityForResult(e.getIntent(), requestCode);
} catch (GoogleAuthException e) {
Log.e(TAG, "GoogleAuthException", e);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
permissionCallback.onFailure();
}
});
} catch (UserRecoverableAuthIOException e) {
activity.startActivityForResult(e.getIntent(), requestCode);
} catch (IOException e) {
Log.e(TAG, "IOException", e);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
permissionCallback.onFailure();
}
});
}
}
});
thread.start();
}
/**
* Sends a notification to request permission.
*
@@ -17,12 +17,14 @@
package com.google.android.apps.mytracks.settings;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.io.sendtogoogle.PermissionCallback;
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
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.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.UserRecoverableAuthIOException;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -37,8 +39,10 @@ import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -49,24 +53,11 @@ import java.util.List;
*/
public class GoogleSettingsActivity extends AbstractSettingsActivity {
private static final String TAG = GoogleSettingsActivity.class.getSimpleName();
private static final String ACCOUNT_NAME_KEY = "accountName";
private static final int DIALOG_CONFIRM_SWITCH_ACCOUNT = 0;
private static final int DIALOG_CONFIRM_DRIVE_SYNC_ON = 1;
private PermissionCallback permissionCallback = new PermissionCallback() {
@Override
public void onSuccess() {
handleSync(true);
}
@Override
public void onFailure() {
Toast.makeText(
GoogleSettingsActivity.this, R.string.send_google_no_account_message, Toast.LENGTH_LONG)
.show();
}
};
private ListPreference googleAccountPreference;
private CheckBoxPreference driveSyncPreference;
@@ -143,9 +134,9 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity {
case SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE:
SendToGoogleUtils.cancelNotification(this, SendToGoogleUtils.DRIVE_NOTIFICATION_ID);
if (resultCode == Activity.RESULT_OK) {
permissionCallback.onSuccess();
onDrivePermissionSuccess();
} else {
permissionCallback.onFailure();
onDrivePermissionFailure();
}
break;
default:
@@ -167,12 +158,37 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity {
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int button) {
String googleAccount = PreferencesUtils.getString(
final String googleAccount = PreferencesUtils.getString(
GoogleSettingsActivity.this, R.string.google_account_key,
PreferencesUtils.GOOGLE_ACCOUNT_DEFAULT);
SendToGoogleUtils.checkPermissionByActivity(GoogleSettingsActivity.this,
googleAccount, SendToGoogleUtils.DRIVE_SCOPE,
SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE, permissionCallback);
/*
* This class, a PreferenceActivity, needs to support api level
* 8+, thus cannot use CheckPermissionFragment because a
* Fragment is only available for api level 11+ and there is no
* support library for PreferenceActivity.
*/
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
SendToGoogleUtils.getGoogleAccountCredential(
GoogleSettingsActivity.this, googleAccount,
SendToGoogleUtils.DRIVE_SCOPE);
handleDriveAccess(true, null);
} catch (UserRecoverableAuthException e) {
handleDriveAccess(false, e.getIntent());
} catch (GoogleAuthException e) {
Log.e(TAG, "GoogleAuthException", e);
handleDriveAccess(false, null);
} catch (UserRecoverableAuthIOException e) {
handleDriveAccess(false, e.getIntent());
} catch (IOException e) {
Log.e(TAG, "IOException", e);
handleDriveAccess(false, null);
}
}
});
thread.start();
}
});
break;
@@ -182,6 +198,33 @@ public class GoogleSettingsActivity extends AbstractSettingsActivity {
return dialog;
}
private void handleDriveAccess(final boolean success, final Intent intent) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (success) {
onDrivePermissionSuccess();
} else {
if (intent != null) {
startActivityForResult(intent, SendToGoogleUtils.DRIVE_PERMISSION_REQUEST_CODE);
} else {
onDrivePermissionFailure();
}
}
}
});
}
private void onDrivePermissionSuccess() {
handleSync(true);
}
private void onDrivePermissionFailure() {
Toast.makeText(
GoogleSettingsActivity.this, R.string.send_google_no_account_message, Toast.LENGTH_LONG)
.show();
}
@Override
protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
AlertDialog alertDialog = (AlertDialog) dialog;
@@ -86,7 +86,7 @@ public class TrackWidgetConfigActivity extends Activity {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(
TrackWidgetConfigActivity.this);
TrackWidgetProvider.updateAppWidget(
TrackWidgetConfigActivity.this, appWidgetManager, appWidgetId);
TrackWidgetConfigActivity.this, appWidgetManager, appWidgetId, -1L);
PreferencesUtils.setInt(TrackWidgetConfigActivity.this, R.string.track_widget_item1,
item1.getSelectedItemPosition());
PreferencesUtils.setInt(TrackWidgetConfigActivity.this, R.string.track_widget_item2,
@@ -82,59 +82,53 @@ public class TrackWidgetProvider extends AppWidgetProvider {
|| context.getString(R.string.track_stopped_broadcast_action).equals(action)
|| context.getString(R.string.track_update_broadcast_action).equals(action)) {
long trackId = intent.getLongExtra(context.getString(R.string.track_id_broadcast_extra), -1L);
updateAllWidgets(context, trackId);
updateAllAppWidgets(context, trackId);
}
}
@Override
public void onEnabled(Context context) {
// Need to update all widgets after phone reboot
updateAllWidgets(context, -1L);
super.onEnabled(context);
// Need to update all app widgets after phone reboot
updateAllAppWidgets(context, -1L);
}
/**
* Updates all widgets.
*
* @param context the context
* @param trackId track id
*/
private void updateAllWidgets(Context context, long trackId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(context, TrackWidgetProvider.class));
for (int appWidgetId : appWidgetIds) {
int size = ApiAdapterFactory.getApiAdapter().getAppWidgetSize(appWidgetManager, appWidgetId);
RemoteViews remoteViews = getRemoteViews(context, trackId, size);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
// Need to update all app widgets after software update
updateAllAppWidgets(context, -1L);
}
@TargetApi(16)
@Override
public void onAppWidgetOptionsChanged(
Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
if (newOptions != null) {
int size;
int newSize;
if (newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1)
== AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
size = 1;
newSize = 1;
} else {
int height = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
if (height == 0) {
size = 2;
newSize = 2;
} else if (height >= FOUR_CELLS) {
size = 4;
newSize = 4;
} else if (height >= THREE_CELLS) {
size = 3;
newSize = 3;
} else if (height >= TWO_CELLS) {
size = 2;
newSize = 2;
} else {
size = 1;
newSize = 1;
}
}
ApiAdapterFactory.getApiAdapter().setAppWidgetSize(appWidgetManager, appWidgetId, size);
RemoteViews remoteViews = getRemoteViews(context, -1L, size);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
int size = ApiAdapterFactory.getApiAdapter().getAppWidgetSize(appWidgetManager, appWidgetId);
if (size != newSize) {
ApiAdapterFactory.getApiAdapter().setAppWidgetSize(appWidgetManager, appWidgetId, newSize);
updateAppWidget(context, appWidgetManager, appWidgetId, -1L);
}
}
}
@@ -144,14 +138,29 @@ public class TrackWidgetProvider extends AppWidgetProvider {
* @param context the context
* @param appWidgetManager the app widget manager
* @param appWidgetId the app widget id
* @param trackId the track id. -1L to not specify one
*/
public static void updateAppWidget(
Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
Context context, AppWidgetManager appWidgetManager, int appWidgetId, long trackId) {
int size = ApiAdapterFactory.getApiAdapter().getAppWidgetSize(appWidgetManager, appWidgetId);
ApiAdapterFactory.getApiAdapter().setAppWidgetSize(appWidgetManager, appWidgetId, size);
RemoteViews remoteViews = getRemoteViews(context, -1L, size);
RemoteViews remoteViews = getRemoteViews(context, trackId, size);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
/**
* Updates all app widgets.
*
* @param context the context
* @param trackId track id
*/
private static void updateAllAppWidgets(Context context, long trackId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(context, TrackWidgetProvider.class));
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId, trackId);
}
}
/**
* Gets the remote views.
@@ -252,11 +252,6 @@ public interface MyTracksProviderUtils {
*/
public Location getLastValidTrackPoint(long trackId);
/**
* Gets the last valid location recorded. Returns null if it doesn't exist.
*/
public Location getLastValidTrackPoint();
/**
* Creates a location cursor. The caller owns the returned cursor and is
* responsible for closing it.
@@ -238,9 +238,9 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
public Track getLastTrack() {
Cursor cursor = null;
try {
String selection = TracksColumns._ID + "=(select max(" + TracksColumns._ID + ") from "
+ TracksColumns.TABLE_NAME + ")";
cursor = getTrackCursor(null, selection, null, TracksColumns._ID);
// Using the same order as shown in the track list
cursor = getTrackCursor(null, null, null,
TracksColumns.SHAREDWITHME + " ASC, " + TracksColumns.STARTTIME + " DESC");
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
@@ -816,14 +816,6 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
return findTrackPointBy(selection, selectionArgs);
}
@Override
public Location getLastValidTrackPoint() {
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID + ") from "
+ TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.LATITUDE + "<="
+ MAX_LATITUDE + ")";
return findTrackPointBy(selection, null);
}
@Override
public Cursor getTrackPointCursor(
long trackId, long startTrackPointId, int maxLocations, boolean descending) {