Bug 1022748 - Part 2: Use custom server URLs in setup activities. r=rnewman

========

2582a39b7f
Author: Nick Alexander <nalexander@mozilla.com>
    Bug 1022748 - Review comments.

========

8c9a8df84f
Author: Nick Alexander <nalexander@mozilla.com>
    Bug 1022748 - Part 4: Don't show Get Started activity when custom server URLs are passed.

    The Get Started activity provides no feedback about custom URLs, and it
    just gets in the way of power users, so skip it entirely.

========

2701a9ea52
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Jun 9 17:25:51 2014 -0700

    Bug 1022748 - Part 3: Pass through server details to update credentials activity.

    This information can be extracted from the AndroidFxAccount object in
    the activity, but feeding the information through extras keeps the
    processing and display pipeline uniform across all setup activities.

========

b9dd884764
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Jun 9 17:17:57 2014 -0700

    Bug 1022748 - Part 2: Use custom server URLs passed through extras in setup activities.

========

be8284ea25
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Jun 9 15:06:03 2014 -0700

    Bug 1022748 - Part 1: Pass extras through setup activities.

    This also passes the password button's show/hide state between create
    and set up.
This commit is contained in:
Nick Alexander 2014-06-21 16:21:41 -07:00
parent 3cc13e1e56
commit 0371bd2e8e
7 changed files with 224 additions and 61 deletions

View File

@ -15,6 +15,7 @@ public class FxAccountConstants {
public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1";
public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
// For extra debugging. Not final so it can be changed from Fennec, or from

View File

@ -23,6 +23,7 @@ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.login.Engaged;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.ProgressDisplay;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.setup.Constants;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
@ -32,6 +33,7 @@ import android.accounts.AccountManager;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
@ -50,6 +52,16 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractActivity implements ProgressDisplay {
public static final String EXTRA_EMAIL = "email";
public static final String EXTRA_PASSWORD = "password";
public static final String EXTRA_PASSWORD_SHOWN = "password_shown";
public static final String EXTRA_YEAR = "year";
public static final String EXTRA_EXTRAS = "extras";
public static final String JSON_KEY_AUTH = "auth";
public static final String JSON_KEY_SERVICES = "services";
public static final String JSON_KEY_SYNC = "sync";
public FxAccountAbstractSetupActivity() {
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST | CANNOT_RESUME_WHEN_LOCKED_OUT);
}
@ -69,33 +81,47 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
protected Button button;
protected ProgressBar progressBar;
private String authServerEndpoint;
private String syncServerEndpoint;
protected String getAuthServerEndpoint() {
return authServerEndpoint;
}
protected String getTokenServerEndpoint() {
return syncServerEndpoint;
}
protected void createShowPasswordButton() {
showPasswordButton.setOnClickListener(new OnClickListener() {
@SuppressWarnings("deprecation")
@Override
public void onClick(View v) {
boolean isShown = passwordEdit.getTransformationMethod() instanceof SingleLineTransformationMethod;
// Changing input type loses position in edit text; let's try to maintain it.
int start = passwordEdit.getSelectionStart();
int stop = passwordEdit.getSelectionEnd();
if (isShown) {
passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
showPasswordButton.setText(R.string.fxaccount_password_show);
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_show_textcolor));
} else {
passwordEdit.setTransformationMethod(SingleLineTransformationMethod.getInstance());
showPasswordButton.setText(R.string.fxaccount_password_hide);
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_hide_textcolor));
}
passwordEdit.setSelection(start, stop);
setPasswordButtonShown(!isShown);
}
});
}
@SuppressWarnings("deprecation")
protected void setPasswordButtonShown(boolean shouldShow) {
// Changing input type loses position in edit text; let's try to maintain it.
int start = passwordEdit.getSelectionStart();
int stop = passwordEdit.getSelectionEnd();
if (!shouldShow) {
passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
showPasswordButton.setText(R.string.fxaccount_password_show);
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_show_textcolor));
} else {
passwordEdit.setTransformationMethod(SingleLineTransformationMethod.getInstance());
showPasswordButton.setText(R.string.fxaccount_password_hide);
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_hide_textcolor));
}
passwordEdit.setSelection(start, stop);
}
protected void linkifyPolicy() {
TextView policyView = (TextView) ensureFindViewById(null, R.id.policy, "policy links");
final String linkTerms = getString(R.string.fxaccount_link_tos);
@ -262,7 +288,7 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
AndroidFxAccount fxAccount;
try {
final String profile = Constants.DEFAULT_PROFILE;
final String tokenServerURI = FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT;
final String tokenServerURI = getTokenServerEndpoint();
// It is crucial that we use the email address provided by the server
// (rather than whatever the user entered), because the user's keys are
// wrapped and salted with the initial email they provided to
@ -356,6 +382,29 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
emailEdit.setAdapter(adapter);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
protected void updateFromIntentExtras() {
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
if (getIntent() != null && getIntent().getExtras() != null) {
Bundle bundle = getIntent().getExtras();
emailEdit.setText(bundle.getString(EXTRA_EMAIL));
passwordEdit.setText(bundle.getString(EXTRA_PASSWORD));
setPasswordButtonShown(bundle.getBoolean(EXTRA_PASSWORD_SHOWN, false));
}
// This sets defaults as well as extracting from extras, so it's not conditional.
updateServersFromIntentExtras(getIntent());
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
FxAccountConstants.pii(LOG_TAG, "Using auth server: " + authServerEndpoint);
FxAccountConstants.pii(LOG_TAG, "Using sync server: " + syncServerEndpoint);
}
}
@Override
public void onResume() {
super.onResume();
@ -370,4 +419,86 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
};
task.execute();
}
protected Bundle makeExtrasBundle(String email, String password) {
final Bundle bundle = new Bundle();
// Pass through any extras that we were started with.
if (getIntent() != null && getIntent().getExtras() != null) {
bundle.putAll(getIntent().getExtras());
}
// Overwrite with current settings.
if (email == null) {
email = emailEdit.getText().toString();
}
if (password == null) {
password = passwordEdit.getText().toString();
}
bundle.putString(EXTRA_EMAIL, email);
bundle.putString(EXTRA_PASSWORD, password);
boolean isPasswordShown = passwordEdit.getTransformationMethod() instanceof SingleLineTransformationMethod;
bundle.putBoolean(EXTRA_PASSWORD_SHOWN, isPasswordShown);
return bundle;
}
protected void startActivityInstead(Class<?> cls, int requestCode, Bundle extras) {
Intent intent = new Intent(this, cls);
if (extras != null) {
intent.putExtras(extras);
}
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(intent, requestCode);
}
protected void updateServersFromIntentExtras(Intent intent) {
// Start with defaults.
this.authServerEndpoint = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
this.syncServerEndpoint = FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT;
if (intent == null) {
Logger.warn(LOG_TAG, "Intent is null; ignoring and using default servers.");
return;
}
final String extrasString = intent.getStringExtra(EXTRA_EXTRAS);
if (extrasString == null) {
return;
}
final ExtendedJSONObject extras;
final ExtendedJSONObject services;
try {
extras = new ExtendedJSONObject(extrasString);
services = extras.getObject(JSON_KEY_SERVICES);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception parsing extras; ignoring and using default servers.");
return;
}
String authServer = extras.getString(JSON_KEY_AUTH);
String syncServer = services == null ? null : services.getString(JSON_KEY_SYNC);
if (authServer != null) {
this.authServerEndpoint = authServer;
}
if (syncServer != null) {
this.syncServerEndpoint = syncServer;
}
if (FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT.equals(syncServerEndpoint) &&
!FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT.equals(authServerEndpoint)) {
// We really don't want to hard-code assumptions about server
// configurations into client code in such a way that if and when the
// situation is relaxed, the client code stops valid usage. Instead, we
// warn. This configuration should present itself as an auth exception at
// Sync time.
Logger.warn(LOG_TAG, "Mozilla's Sync token servers only works with Mozilla's auth servers. Sync will likely be mis-configured.");
}
}
}

View File

@ -89,32 +89,29 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
signInInsteadLink.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String email = emailEdit.getText().toString();
final String password = passwordEdit.getText().toString();
doSigninInstead(email, password);
final Bundle extras = makeExtrasBundle(null, null);
startActivityInstead(FxAccountSignInActivity.class, CHILD_REQUEST_CODE, extras);
}
});
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
if (getIntent() != null && getIntent().getExtras() != null) {
Bundle bundle = getIntent().getExtras();
emailEdit.setText(bundle.getString("email"));
passwordEdit.setText(bundle.getString("password"));
}
updateFromIntentExtras();
}
protected void doSigninInstead(final String email, final String password) {
Intent intent = new Intent(this, FxAccountSignInActivity.class);
if (email != null) {
intent.putExtra("email", email);
@Override
protected Bundle makeExtrasBundle(String email, String password) {
final Bundle extras = super.makeExtrasBundle(email, password);
final String year = yearEdit.getText().toString();
extras.putString(EXTRA_YEAR, year);
return extras;
}
@Override
protected void updateFromIntentExtras() {
super.updateFromIntentExtras();
if (getIntent() != null) {
yearEdit.setText(getIntent().getStringExtra(EXTRA_YEAR));
}
if (password != null) {
intent.putExtra("password", password);
}
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(intent, CHILD_REQUEST_CODE);
}
@Override
@ -142,7 +139,9 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
email = emailEdit.getText().toString();
}
final String password = passwordEdit.getText().toString();
doSigninInstead(email, password);
final Bundle extras = makeExtrasBundle(email, password);
startActivityInstead(FxAccountSignInActivity.class, CHILD_REQUEST_CODE, extras);
}
}, clickableStart, clickableEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
remoteErrorTextView.setMovementMethod(LinkMovementMethod.getInstance());
@ -205,7 +204,7 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
}
public void createAccount(String email, String password, Map<String, Boolean> engines) {
String serverURI = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
String serverURI = getAuthServerEndpoint();
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
// This delegate creates a new Android account on success, opens the
// appropriate "success!" activity, and finishes this activity.

View File

@ -50,15 +50,26 @@ public class FxAccountGetStartedActivity extends AccountAuthenticatorActivity {
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FxAccountGetStartedActivity.this, FxAccountCreateAccountActivity.class);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(intent, CHILD_REQUEST_CODE);
Bundle extras = null; // startFlow accepts null.
if (getIntent() != null) {
extras = getIntent().getExtras();
}
startFlow(extras);
}
});
}
protected void startFlow(Bundle extras) {
final Intent intent = new Intent(this, FxAccountCreateAccountActivity.class);
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
if (extras != null) {
intent.putExtras(extras);
}
startActivityForResult(intent, CHILD_REQUEST_CODE);
}
@Override
public void onResume() {
super.onResume();
@ -79,6 +90,20 @@ public class FxAccountGetStartedActivity extends AccountAuthenticatorActivity {
this.startActivity(intent);
this.finish();
}
// If we've been launched with extras (namely custom server URLs), continue
// past go and collect 200 dollars. If we ever get back here (for example,
// if the user hits the back button), forget that we had extras entirely, so
// that we don't enter a loop.
Bundle extras = null;
if (getIntent() != null) {
extras = getIntent().getExtras();
}
if (extras != null && extras.containsKey(FxAccountAbstractSetupActivity.EXTRA_EXTRAS)) {
getIntent().replaceExtras(Bundle.EMPTY);
startFlow((Bundle) extras.clone());
return;
}
}
/**

View File

@ -15,7 +15,6 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20;
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
import org.mozilla.gecko.background.fxa.PasswordStretcher;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
@ -65,22 +64,12 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
createAccountInsteadLink.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FxAccountSignInActivity.this, FxAccountCreateAccountActivity.class);
intent.putExtra("email", emailEdit.getText().toString());
intent.putExtra("password", passwordEdit.getText().toString());
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(intent, CHILD_REQUEST_CODE);
final Bundle extras = makeExtrasBundle(null, null);
startActivityInstead(FxAccountCreateAccountActivity.class, CHILD_REQUEST_CODE, extras);
}
});
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
if (getIntent() != null && getIntent().getExtras() != null) {
Bundle bundle = getIntent().getExtras();
emailEdit.setText(bundle.getString("email"));
passwordEdit.setText(bundle.getString("password"));
}
updateFromIntentExtras();
TextView view = (TextView) findViewById(R.id.forgot_password_link);
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
@ -102,7 +91,7 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
}
public void signIn(String email, String password) {
String serverURI = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
String serverURI = getAuthServerEndpoint();
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
// This delegate creates a new Android account on success, opens the
// appropriate "success!" activity, and finishes this activity.

View File

@ -18,6 +18,7 @@ import org.mozilla.gecko.fxa.login.Married;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
import org.mozilla.gecko.sync.SyncConfiguration;
@ -152,6 +153,10 @@ public class FxAccountStatusFragment
public boolean onPreferenceClick(Preference preference) {
if (preference == needsPasswordPreference) {
Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
final Bundle extras = getExtrasForAccount();
if (extras != null) {
intent.putExtras(extras);
}
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
// the soft keyboard not being shown for the started activity. Why, Android, why?
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
@ -190,6 +195,17 @@ public class FxAccountStatusFragment
return false;
}
protected Bundle getExtrasForAccount() {
final Bundle extras = new Bundle();
final ExtendedJSONObject o = new ExtendedJSONObject();
o.put(FxAccountAbstractSetupActivity.JSON_KEY_AUTH, fxAccount.getAccountServerURI());
final ExtendedJSONObject services = new ExtendedJSONObject();
services.put(FxAccountAbstractSetupActivity.JSON_KEY_SYNC, fxAccount.getTokenServerURI());
o.put(FxAccountAbstractSetupActivity.JSON_KEY_SERVICES, services);
extras.putString(FxAccountAbstractSetupActivity.EXTRA_EXTRAS, o.toJSONString());
return extras;
}
protected void setCheckboxesEnabled(boolean enabled) {
bookmarksPreference.setEnabled(enabled);
historyPreference.setEnabled(enabled);

View File

@ -77,6 +77,8 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
TextView view = (TextView) findViewById(R.id.forgot_password_link);
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
updateFromIntentExtras();
}
@Override