diff --git a/MyTracks/AndroidManifest.xml b/MyTracks/AndroidManifest.xml index bac3f32dd..0f6250419 100755 --- a/MyTracks/AndroidManifest.xml +++ b/MyTracks/AndroidManifest.xml @@ -95,7 +95,7 @@ limitations under the License. - + - + + minRecordingInterval minRequiredAccuracy pickExistingMap + preferredAccount reportSpeed sendToDocs sendToFusionTables diff --git a/MyTracks/src/com/google/android/apps/mytracks/AccountChooser.java b/MyTracks/src/com/google/android/apps/mytracks/AccountChooser.java index bc8dc5707..f7e886106 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/AccountChooser.java +++ b/MyTracks/src/com/google/android/apps/mytracks/AccountChooser.java @@ -26,7 +26,10 @@ import android.util.Log; /** * Choose which account to upload track information to. + * * @author Sandor Dornbush + * + * @deprecated To be removed. */ public class AccountChooser { diff --git a/MyTracks/src/com/google/android/apps/mytracks/Constants.java b/MyTracks/src/com/google/android/apps/mytracks/Constants.java index 5081622f8..a6d2a3df8 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/Constants.java +++ b/MyTracks/src/com/google/android/apps/mytracks/Constants.java @@ -35,8 +35,11 @@ public abstract class Constants { /* * onActivityResult request codes: + * + * TODO: These should be moved to the + * activities that call startActivityForResult. Thus do not need to keep a + * global order across all activities. */ - public static final int GET_LOGIN = 0; public static final int GET_MAP = 1; public static final int SHOW_TRACK = 2; @@ -54,6 +57,7 @@ public abstract class Constants { public static final int SAVE_TCX_FILE = 14; public static final int SHOW_WAYPOINT = 15; public static final int WELCOME = 16; + public static final int CHOOSE_ACCOUNT = 17; /* * Menu ids: diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/AccountChooserActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/AccountChooserActivity.java new file mode 100644 index 000000000..89c09243d --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/AccountChooserActivity.java @@ -0,0 +1,168 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.io.sendtogoogle; + +import com.google.android.apps.mytracks.Constants; +import com.google.android.apps.mytracks.util.ApiFeatures; +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.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; + +/** + * A chooser to select an account. To be called with + * {@link Activity#startActivityForResult(Intent, int)}. Returns + * {@link Activity#RESULT_CANCELED} if the user cancels the activity. Otherwise, + * returns {@link Activity#RESULT_OK} with an intent containing the selected + * account. The selected account is stored as an {@link Account} in the + * {@link AccountChooserActivity#ACCOUNT} item of the intent. The selected + * account can be null. + * + * @author jshih@google.com (Jimmy Shih) + */ +public class AccountChooserActivity extends Activity { + + /** + * Key for storing an {@link Account} in an {@link Intent}. + */ + public static final String ACCOUNT = "account"; + + private static final int NO_ACCOUNT_DIALOG = 1; + private static final int CHOOSE_ACCOUNT_DIALOG = 2; + + private Account[] accounts; + private int selectedAccountIndex; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + accounts = AccountManager.get(this).getAccountsByType(Constants.ACCOUNT_TYPE); + + if (accounts.length == 1) { + Intent intent = new Intent(); + intent.putExtra(ACCOUNT, accounts[0]); + setResult(RESULT_OK, intent); + finish(); + } + + SharedPreferences prefs = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE); + String preferredAccount = prefs.getString(getString(R.string.preferred_account_key), ""); + + selectedAccountIndex = -1; + for (int i = 0; i < accounts.length; i++) { + if (accounts[i].name.equals(preferredAccount)) { + selectedAccountIndex = i; + break; + } + } + } + + @Override + protected void onResume() { + super.onResume(); + if (accounts.length == 0) { + showDialog(NO_ACCOUNT_DIALOG); + } else { + showDialog(CHOOSE_ACCOUNT_DIALOG); + } + } + + @Override + protected Dialog onCreateDialog(int id) { + AlertDialog.Builder builder; + switch (id) { + case NO_ACCOUNT_DIALOG: + builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.send_google_no_account_title); + builder.setMessage(R.string.send_google_no_account_message); + builder.setCancelable(true); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + setResult(RESULT_CANCELED); + finish(); + } + }); + builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(); + intent.putExtra(ACCOUNT, (Account) null); + setResult(RESULT_OK, intent); + finish(); + } + }); + return builder.create(); + case CHOOSE_ACCOUNT_DIALOG: + builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.send_google_choose_account_title); + + String[] choices = new String[accounts.length]; + for (int i = 0; i < accounts.length; i++) { + choices[i] = accounts[i].name; + } + builder.setSingleChoiceItems(choices, selectedAccountIndex, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + selectedAccountIndex = which; + } + }); + + builder.setCancelable(true); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + setResult(RESULT_CANCELED); + finish(); + } + }); + builder.setNegativeButton(R.string.generic_cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + setResult(RESULT_CANCELED); + finish(); + } + }); + builder.setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Account account = null; + if (selectedAccountIndex != -1) { + account = accounts[selectedAccountIndex]; + SharedPreferences prefs = getSharedPreferences( + Constants.SETTINGS_NAME, Context.MODE_PRIVATE); + Editor editor = prefs.edit(); + editor.putString(getString(R.string.preferred_account_key), account.name); + ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor); + } + Intent intent = new Intent(); + intent.putExtra(ACCOUNT, account); + setResult(RESULT_OK, intent); + finish(); + } + }); + return builder.create(); + default: + return null; + } + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendActivity.java index 404ff0d05..dad6abe82 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/SendActivity.java @@ -18,7 +18,6 @@ package com.google.android.apps.mytracks.io.sendtogoogle; import static com.google.android.apps.mytracks.Constants.TAG; import com.google.android.apps.analytics.GoogleAnalyticsTracker; -import com.google.android.apps.mytracks.AccountChooser; import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.MapsList; import com.google.android.apps.mytracks.ProgressIndicator; @@ -60,6 +59,7 @@ import java.util.List; */ public class SendActivity extends Activity implements ProgressIndicator { + // Items in the intent that starts the activity. public static final String TRACK_ID = "trackId"; public static final String SHARE_URL = "shareUrl"; public static final String SEND_MAPS = "sendMaps"; @@ -68,20 +68,17 @@ public class SendActivity extends Activity implements ProgressIndicator { public static final String CREATE_MAP = "createMap"; // Keys for saved state variables. - private static final String STATE_SEND_TO_MAPS = "mapsSend"; - private static final String STATE_SEND_TO_FUSION_TABLES = "fusionSend"; - private static final String STATE_SEND_TO_DOCS = "docsSend"; + private static final String STATE_ACCOUNT = "account"; + private static final String STATE_STATE = "state"; private static final String STATE_DOCS_SUCCESS = "docsSuccess"; private static final String STATE_FUSION_SUCCESS = "fusionSuccess"; private static final String STATE_MAPS_SUCCESS = "mapsSuccess"; - private static final String STATE_STATE = "state"; - private static final String STATE_ACCOUNT_TYPE = "accountType"; - private static final String STATE_ACCOUNT_NAME = "accountName"; private static final String STATE_TABLE_ID = "tableId"; private static final String STATE_MAP_ID = "mapId"; /** States for the state machine that defines the upload process. */ private enum SendState { + CHOOSE_ACCOUNT, START, AUTHENTICATE_MAPS, PICK_MAP, @@ -108,37 +105,32 @@ public class SendActivity extends Activity implements ProgressIndicator { // UI private ProgressDialog progressDialog; - // Services + // Set in Activity.onCreate private MyTracksProviderUtils providerUtils; private SharedPreferences sharedPreferences; private GoogleAnalyticsTracker tracker; - // Authentication - private AuthManager lastAuth; - private final HashMap authMap = new HashMap(); - private AccountChooser accountChooser; - private String lastAccountName; - private String lastAccountType; - - // Send request information. + // Send request information. Set by the intent that starts the activity. private long sendTrackId; private boolean shareRequest; private boolean sendToMaps; private boolean sendToMapsNewMap; private boolean sendToFusionTables; private boolean sendToDocs; - - // Send result information, used by results dialog. - private boolean sendToMapsSuccess = false; - private boolean sendToFusionTablesSuccess = false; - private boolean sendToDocsSuccess = false; - - // Send result information, used to share a link. - private String sendToMapsMapId; - private String sendToFusionTablesTableId; - + + // Authentication information. + private Account account; + private HashMap authMap = new HashMap(); + // Current sending state. private SendState currentState; + + // Send result information. Used by the results dialog. + private boolean sendToMapsSuccess; + private boolean sendToFusionTablesSuccess; + private boolean sendToDocsSuccess; + private String sendToMapsMapId; + private String sendToFusionTablesTableId; private final OnCancelListener finishOnCancelListener = new OnCancelListener() { @Override @@ -174,7 +166,7 @@ public class SendActivity extends Activity implements ProgressIndicator { } // Only consider the intent if we're not restoring from a previous state. - if (currentState == SendState.START) { + if (currentState == SendState.CHOOSE_ACCOUNT) { if (!handleIntent()) { finish(); return; @@ -215,41 +207,29 @@ public class SendActivity extends Activity implements ProgressIndicator { } private void restoreInstanceState(Bundle savedInstanceState) { + account = savedInstanceState.getParcelable(STATE_ACCOUNT); + currentState = SendState.values()[savedInstanceState.getInt(STATE_STATE)]; - sendToMaps = savedInstanceState.getBoolean(STATE_SEND_TO_MAPS); - sendToFusionTables = savedInstanceState.getBoolean(STATE_SEND_TO_FUSION_TABLES); - sendToDocs = savedInstanceState.getBoolean(STATE_SEND_TO_DOCS); - sendToMapsSuccess = savedInstanceState.getBoolean(STATE_MAPS_SUCCESS); sendToFusionTablesSuccess = savedInstanceState.getBoolean(STATE_FUSION_SUCCESS); sendToDocsSuccess = savedInstanceState.getBoolean(STATE_DOCS_SUCCESS); - sendToMapsMapId = savedInstanceState.getString(STATE_MAP_ID); sendToFusionTablesTableId = savedInstanceState.getString(STATE_TABLE_ID); - - lastAccountName = savedInstanceState.getString(STATE_ACCOUNT_NAME); - lastAccountType = savedInstanceState.getString(STATE_ACCOUNT_TYPE); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - + outState.putParcelable(STATE_ACCOUNT, account); + outState.putInt(STATE_STATE, currentState.ordinal()); outState.putBoolean(STATE_MAPS_SUCCESS, sendToMapsSuccess); outState.putBoolean(STATE_FUSION_SUCCESS, sendToFusionTablesSuccess); outState.putBoolean(STATE_DOCS_SUCCESS, sendToDocsSuccess); - outState.putString(STATE_MAP_ID, sendToMapsMapId); outState.putString(STATE_TABLE_ID, sendToFusionTablesTableId); - - outState.putString(STATE_ACCOUNT_NAME, lastAccountName); - outState.putString(STATE_ACCOUNT_TYPE, lastAccountType); - - // TODO: Ideally we should serialize/restore the authenticator map and lastAuth somehow, - // but it's highly unlikely we'll get killed while an auth dialog is displayed. } @Override @@ -282,6 +262,8 @@ public class SendActivity extends Activity implements ProgressIndicator { private SendState executeState(SendState state) { switch (state) { + case CHOOSE_ACCOUNT: + return chooseAccount(); case START: return startSend(); case AUTHENTICATE_MAPS: @@ -318,6 +300,12 @@ public class SendActivity extends Activity implements ProgressIndicator { } } + private SendState chooseAccount() { + Intent intent = new Intent(this, AccountChooserActivity.class); + startActivityForResult(intent, Constants.CHOOSE_ACCOUNT); + return SendState.NOT_READY; + } + /** * Initiates the process to send tracks to google. * This is called once the user has selected sending options via the @@ -363,8 +351,8 @@ public class SendActivity extends Activity implements ProgressIndicator { if (!sendToMapsNewMap) { // Ask the user to choose a map to upload into Intent listIntent = new Intent(this, MapsList.class); - listIntent.putExtra(MapsList.EXTRA_ACCOUNT_NAME, lastAccountName); - listIntent.putExtra(MapsList.EXTRA_ACCOUNT_TYPE, lastAccountType); + listIntent.putExtra(MapsList.EXTRA_ACCOUNT_NAME, account.name); + listIntent.putExtra(MapsList.EXTRA_ACCOUNT_TYPE, account.type); startActivityForResult(listIntent, Constants.GET_MAP); // The callback for GET_MAP calls authenticateToGoogleMaps return SendState.NOT_READY; @@ -406,8 +394,9 @@ public class SendActivity extends Activity implements ProgressIndicator { sendToMapsMapId = SendToMaps.NEW_MAP_ID; } - final SendToMaps sender = new SendToMaps(this, sendToMapsMapId, lastAuth, - sendTrackId, this /*progressIndicator*/, onCompletion); + final SendToMaps sender = new SendToMaps(this, sendToMapsMapId, + getAuthManager(MapsConstants.SERVICE_NAME), sendTrackId, this /*progressIndicator*/, + onCompletion); new Thread(sender, "SendToMaps").start(); @@ -461,8 +450,9 @@ public class SendActivity extends Activity implements ProgressIndicator { } }; - final SendToFusionTables sender = new SendToFusionTables(this, lastAuth, - sendTrackId, this /*progressIndicator*/, onCompletion); + final SendToFusionTables sender = new SendToFusionTables( + this, getAuthManager(SendToFusionTables.SERVICE_ID), sendTrackId, + this /* progressIndicator */, onCompletion); new Thread(sender, "SendToFusionTables").start(); @@ -502,8 +492,8 @@ public class SendActivity extends Activity implements ProgressIndicator { String serviceName = getString(SendType.DOCS.getServiceName()); setProgressMessage(String.format(format, serviceName)); final SendToDocs sender = new SendToDocs(this, - authMap.get(SendToDocs.GDATA_SERVICE_NAME_TRIX), - authMap.get(SendToDocs.GDATA_SERVICE_NAME_DOCLIST), + getAuthManager(SendToDocs.GDATA_SERVICE_NAME_TRIX), + getAuthManager(SendToDocs.GDATA_SERVICE_NAME_DOCLIST), this); Runnable onCompletion = new Runnable() { public void run() { @@ -671,59 +661,8 @@ public class SendActivity extends Activity implements ProgressIndicator { * token, prompting the user for a login and password if needed. */ private void authenticate(final int requestCode, final String service) { - lastAuth = authMap.get(service); - if (lastAuth == null) { - Log.i(TAG, "Creating a new authentication for service: " + service); - lastAuth = AuthManagerFactory.getAuthManager(this, - Constants.GET_LOGIN, - null, - true, - service); - authMap.put(service, lastAuth); - } - - Log.d(TAG, "Logging in to " + service + "..."); - if (AuthManagerFactory.useModernAuthManager()) { - runOnUiThread(new Runnable() { - @Override - public void run() { - chooseAccount(requestCode, service); - } - }); - } else { - doLogin(requestCode, service, null); - } - } - - private void chooseAccount(final int requestCode, final String service) { - if (accountChooser == null) { - accountChooser = new AccountChooser(); - - // Restore state if necessary. - if (lastAccountName != null && lastAccountType != null) { - accountChooser.setChosenAccount(lastAccountName, lastAccountType); - } - } - - accountChooser.chooseAccount(SendActivity.this, - new AccountChooser.AccountHandler() { - @Override - public void onAccountSelected(Account account) { - if (account == null) { - dismissDialog(PROGRESS_DIALOG); - finish(); - return; - } - - lastAccountName = account.name; - lastAccountType = account.type; - doLogin(requestCode, service, account); - } - }); - } - - private void doLogin(final int requestCode, final String service, final Object account) { - lastAuth.doLogin(new AuthCallback() { + AuthManager authManager = getAuthManager(service); + authManager.doLogin(new AuthCallback() { @Override public void onAuthResult(boolean success) { Log.i(TAG, "Login success for " + service + ": " + success); @@ -742,16 +681,6 @@ public class SendActivity extends Activity implements ProgressIndicator { final Intent results) { SendState nextState = null; switch (requestCode) { - case Constants.GET_LOGIN: { - if (resultCode == RESULT_CANCELED || lastAuth == null) { - nextState = SendState.FINISH; - break; - } - - // This will invoke onAuthResult appropriately. - lastAuth.authResult(resultCode, results); - break; - } case Constants.GET_MAP: { // User picked a map to upload to Log.d(TAG, "Get map result: " + resultCode); @@ -765,6 +694,19 @@ public class SendActivity extends Activity implements ProgressIndicator { } break; } + case Constants.CHOOSE_ACCOUNT: { + if (resultCode == RESULT_CANCELED) { + nextState = SendState.FINISH; + break; + } + account = results.getParcelableExtra(AccountChooserActivity.ACCOUNT); + if (account == null) { + nextState = SendState.FINISH; + break; + } + nextState = SendState.START; + break; + } default: { Log.e(TAG, "Unrequested result: " + requestCode); return; @@ -805,15 +747,29 @@ public class SendActivity extends Activity implements ProgressIndicator { executeStateMachine(nextState); } + private AuthManager getAuthManager(String service) { + AuthManager authManager = authMap.get(service); + if (authManager == null) { + authManager = AuthManagerFactory.getAuthManager(this, + Constants.GET_LOGIN, + null, + true, + service); + authMap.put(service, authManager); + } + return authManager; + } /** * Resets status information for sending to Maps/Fusion Tables/Docs. */ private void resetState() { - currentState = SendState.START; + account = null; + authMap.clear(); + currentState = SendState.CHOOSE_ACCOUNT; + sendToMapsSuccess = false; + sendToFusionTablesSuccess = false; + sendToDocsSuccess = false; sendToMapsMapId = null; - sendToMapsSuccess = true; - sendToFusionTablesSuccess = true; - sendToDocsSuccess = true; sendToFusionTablesTableId = null; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java index 01241687d..b6f3fc8d1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java @@ -88,12 +88,6 @@ public class UploadServiceChooserActivity extends Activity { showDialog(SERVICE_PICKER_DIALOG); } - @Override - protected void onPause() { - super.onPause(); - removeDialog(SERVICE_PICKER_DIALOG); - } - @Override protected Dialog onCreateDialog(int id) { switch (id) {