Bug 951304 - Part 2: first cut at native UI for Firefox Accounts on Android. r=rnewman
@ -541,7 +541,15 @@ sync_java_files = [
|
||||
'browserid/verifier/BrowserIDVerifierDelegate.java',
|
||||
'browserid/verifier/BrowserIDVerifierException.java',
|
||||
'browserid/VerifyingPublicKey.java',
|
||||
'fxa/activities/FxAccountSetupActivity.java',
|
||||
'fxa/activities/FxAccountAbstractActivity.java',
|
||||
'fxa/activities/FxAccountAbstractSetupActivity.java',
|
||||
'fxa/activities/FxAccountCreateAccountActivity.java',
|
||||
'fxa/activities/FxAccountCreateAccountFragment.java',
|
||||
'fxa/activities/FxAccountCreateSuccessActivity.java',
|
||||
'fxa/activities/FxAccountGetStartedActivity.java',
|
||||
'fxa/activities/FxAccountSetupTask.java',
|
||||
'fxa/activities/FxAccountSignInActivity.java',
|
||||
'fxa/activities/FxAccountStatusActivity.java',
|
||||
'fxa/authenticator/AbstractFxAccount.java',
|
||||
'fxa/authenticator/AndroidFxAccount.java',
|
||||
'fxa/authenticator/FxAccountAuthenticator.java',
|
||||
@ -549,7 +557,6 @@ sync_java_files = [
|
||||
'fxa/authenticator/FxAccountLoginDelegate.java',
|
||||
'fxa/authenticator/FxAccountLoginException.java',
|
||||
'fxa/authenticator/FxAccountLoginPolicy.java',
|
||||
'fxa/sync/FxAccount.java',
|
||||
'fxa/sync/FxAccountGlobalSession.java',
|
||||
'fxa/sync/FxAccountSyncAdapter.java',
|
||||
'fxa/sync/FxAccountSyncService.java',
|
||||
|
@ -0,0 +1,80 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
public abstract class FxAccountAbstractActivity extends Activity {
|
||||
private static final String LOG_TAG = FxAccountAbstractActivity.class.getSimpleName();
|
||||
|
||||
protected void launchActivity(Class<? extends Activity> activityClass) {
|
||||
Intent intent = new Intent(this, activityClass);
|
||||
// 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);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
protected void redirectToActivity(Class<? extends Activity> activityClass) {
|
||||
launchActivity(activityClass);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to find view or error if it is missing.
|
||||
*
|
||||
* @param id of view to find.
|
||||
* @param description to print in error.
|
||||
* @return non-null <code>View</code> instance.
|
||||
*/
|
||||
public View ensureFindViewById(View v, int id, String description) {
|
||||
View view;
|
||||
if (v != null) {
|
||||
view = v.findViewById(id);
|
||||
} else {
|
||||
view = findViewById(id);
|
||||
}
|
||||
if (view == null) {
|
||||
String message = "Could not find view " + description + ".";
|
||||
Logger.error(LOG_TAG, message);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public void linkifyTextViews(View view, int[] textViews) {
|
||||
for (int id : textViews) {
|
||||
TextView textView;
|
||||
if (view != null) {
|
||||
textView = (TextView) view.findViewById(id);
|
||||
} else {
|
||||
textView = (TextView) findViewById(id);
|
||||
}
|
||||
if (textView == null) {
|
||||
Logger.warn(LOG_TAG, "Could not process links for view with id " + id + ".");
|
||||
continue;
|
||||
}
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
textView.setText(Html.fromHtml(textView.getText().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
protected void launchActivityOnClick(final View view, final Class<? extends Activity> activityClass) {
|
||||
view.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FxAccountAbstractActivity.this.launchActivity(activityClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Patterns;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractActivity {
|
||||
private static final String LOG_TAG = FxAccountAbstractSetupActivity.class.getSimpleName();
|
||||
|
||||
protected int minimumPasswordLength = 8;
|
||||
|
||||
protected TextView localErrorTextView;
|
||||
protected EditText emailEdit;
|
||||
protected EditText passwordEdit;
|
||||
protected Button showPasswordButton;
|
||||
protected Button button;
|
||||
|
||||
protected void createShowPasswordButton() {
|
||||
showPasswordButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean isShown = 0 == (passwordEdit.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
// Changing input type loses position in edit text; let's try to maintain it.
|
||||
int start = passwordEdit.getSelectionStart();
|
||||
int stop = passwordEdit.getSelectionEnd();
|
||||
passwordEdit.setInputType(passwordEdit.getInputType() ^ InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
passwordEdit.setSelection(start, stop);
|
||||
if (isShown) {
|
||||
showPasswordButton.setText(R.string.fxaccount_password_show);
|
||||
} else {
|
||||
showPasswordButton.setText(R.string.fxaccount_password_hide);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void showRemoteError(Exception e) {
|
||||
new AlertDialog.Builder(this).setTitle("Remote error!").setMessage(e.toString()).show();
|
||||
}
|
||||
|
||||
protected void addListeners() {
|
||||
TextChangedListener textChangedListener = new TextChangedListener();
|
||||
EditorActionListener editorActionListener = new EditorActionListener();
|
||||
FocusChangeListener focusChangeListener = new FocusChangeListener();
|
||||
|
||||
emailEdit.addTextChangedListener(textChangedListener);
|
||||
emailEdit.setOnEditorActionListener(editorActionListener);
|
||||
emailEdit.setOnFocusChangeListener(focusChangeListener);
|
||||
passwordEdit.addTextChangedListener(textChangedListener);
|
||||
passwordEdit.setOnEditorActionListener(editorActionListener);
|
||||
passwordEdit.setOnFocusChangeListener(focusChangeListener);
|
||||
}
|
||||
|
||||
protected class FocusChangeListener implements OnFocusChangeListener {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
return;
|
||||
}
|
||||
updateButtonState();
|
||||
}
|
||||
}
|
||||
|
||||
protected class EditorActionListener implements OnEditorActionListener {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
updateButtonState();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected class TextChangedListener implements TextWatcher {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
updateButtonState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean updateButtonState() {
|
||||
final String email = emailEdit.getText().toString();
|
||||
final String password = passwordEdit.getText().toString();
|
||||
|
||||
boolean enabled =
|
||||
(email.length() > 0) &&
|
||||
Patterns.EMAIL_ADDRESS.matcher(email).matches() &&
|
||||
(password.length() >= minimumPasswordLength);
|
||||
|
||||
if (enabled != button.isEnabled()) {
|
||||
Logger.debug(LOG_TAG, (enabled ? "En" : "Dis") + "abling button.");
|
||||
button.setEnabled(enabled);
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignUpTask;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator;
|
||||
import org.mozilla.gecko.sync.HTTPFailureException;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
|
||||
/**
|
||||
* Activity which displays create account screen to the user.
|
||||
*/
|
||||
public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivity {
|
||||
protected static final String LOG_TAG = FxAccountCreateAccountActivity.class.getSimpleName();
|
||||
|
||||
protected EditText yearEdit;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_create_account);
|
||||
|
||||
linkifyTextViews(null, new int[] { R.id.policy });
|
||||
|
||||
localErrorTextView = (TextView) ensureFindViewById(null, R.id.local_error, "local error text view");
|
||||
emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
|
||||
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
|
||||
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
|
||||
yearEdit = (EditText) ensureFindViewById(null, R.id.year_edit, "year edit");
|
||||
button = (Button) ensureFindViewById(null, R.id.create_account_button, "create account button");
|
||||
|
||||
createCreateAccountButton();
|
||||
createYearEdit();
|
||||
addListeners();
|
||||
updateButtonState();
|
||||
createShowPasswordButton();
|
||||
|
||||
launchActivityOnClick(ensureFindViewById(null, R.id.sign_in_instead_link, "sign in instead link"), FxAccountSignInActivity.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (FxAccountAuthenticator.getFirefoxAccounts(this).length > 0) {
|
||||
redirectToActivity(FxAccountStatusActivity.class);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected void createYearEdit() {
|
||||
yearEdit.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String[] years = new String[20];
|
||||
for (int i = 0; i < years.length; i++) {
|
||||
years[i] = Integer.toString(2014 - i);
|
||||
}
|
||||
|
||||
android.content.DialogInterface.OnClickListener listener = new Dialog.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
yearEdit.setText(years[which]);
|
||||
}
|
||||
};
|
||||
|
||||
AlertDialog dialog = new AlertDialog.Builder(FxAccountCreateAccountActivity.this)
|
||||
.setTitle(R.string.fxaccount_when_were_you_born)
|
||||
.setItems(years, listener)
|
||||
.setIcon(R.drawable.fxaccount_icon)
|
||||
.create();
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected class CreateAccountDelegate implements RequestDelegate<String> {
|
||||
public final String email;
|
||||
public final String password;
|
||||
public final String serverURI;
|
||||
|
||||
public CreateAccountDelegate(String email, String password, String serverURI) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this.serverURI = serverURI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
showRemoteError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(int status, HttpResponse response) {
|
||||
handleError(new HTTPFailureException(new SyncStorageResponse(response)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(String result) {
|
||||
Activity activity = FxAccountCreateAccountActivity.this;
|
||||
Logger.info(LOG_TAG, "Got success creating account.");
|
||||
|
||||
// We're on the UI thread, but it's okay to create the account here.
|
||||
Account account;
|
||||
try {
|
||||
account = AndroidFxAccount.addAndroidAccount(activity, email, password,
|
||||
serverURI, null, null, false);
|
||||
if (account == null) {
|
||||
throw new RuntimeException("XXX what?");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// For great debugging.
|
||||
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
|
||||
new AndroidFxAccount(activity, account).dump();
|
||||
}
|
||||
|
||||
Toast.makeText(getApplicationContext(), "Got success creating account.", Toast.LENGTH_LONG).show();
|
||||
redirectToActivity(FxAccountStatusActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void createAccount(String email, String password) {
|
||||
String serverURI = FxAccountConstants.DEFAULT_IDP_ENDPOINT;
|
||||
RequestDelegate<String> delegate = new CreateAccountDelegate(email, password, serverURI);
|
||||
Executor executor = Executors.newSingleThreadExecutor();
|
||||
FxAccountClient20 client = new FxAccountClient20(serverURI, executor);
|
||||
try {
|
||||
new FxAccountSignUpTask(this, email, password, client, delegate).execute();
|
||||
} catch (Exception e) {
|
||||
showRemoteError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createCreateAccountButton() {
|
||||
button.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!updateButtonState()) {
|
||||
return;
|
||||
}
|
||||
final String email = emailEdit.getText().toString();
|
||||
final String password = passwordEdit.getText().toString();
|
||||
createAccount(email, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
public class FxAccountCreateAccountFragment extends Fragment { // implements OnClickListener {
|
||||
protected static final String LOG_TAG = FxAccountCreateAccountFragment.class.getSimpleName();
|
||||
//
|
||||
// protected FxAccountSetupActivity activity;
|
||||
//
|
||||
// protected EditText emailEdit;
|
||||
// protected EditText passwordEdit;
|
||||
// protected EditText password2Edit;
|
||||
// protected Button button;
|
||||
//
|
||||
// protected TextView emailError;
|
||||
// protected TextView passwordError;
|
||||
//
|
||||
// protected TextChangedListener textChangedListener;
|
||||
// protected EditorActionListener editorActionListener;
|
||||
// protected OnFocusChangeListener focusChangeListener;
|
||||
// @Override
|
||||
// public void onCreate(Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// // Retain this fragment across configuration changes. See, for example,
|
||||
// // http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
|
||||
// // This fragment will own AsyncTask instances which should not be
|
||||
// // interrupted by configuration changes (and activity changes).
|
||||
// setRetainInstance(true);
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
// Bundle savedInstanceState) {
|
||||
// View v = inflater.inflate(R.layout.fxaccount_create_account_fragment, container, false);
|
||||
//
|
||||
// FxAccountSetupActivity.linkifyTextViews(v, new int[] { R.id.description, R.id.policy });
|
||||
//
|
||||
// emailEdit = (EditText) ensureFindViewById(v, R.id.email, "email");
|
||||
// passwordEdit = (EditText) ensureFindViewById(v, R.id.password, "password");
|
||||
// // Second password can be null.
|
||||
// password2Edit = (EditText) v.findViewById(R.id.password2);
|
||||
//
|
||||
// emailError = (TextView) ensureFindViewById(v, R.id.email_error, "email error");
|
||||
// passwordError = (TextView) ensureFindViewById(v, R.id.password_error, "password error");
|
||||
//
|
||||
// textChangedListener = new TextChangedListener();
|
||||
// editorActionListener = new EditorActionListener();
|
||||
// focusChangeListener = new FocusChangeListener();
|
||||
//
|
||||
// addListeners(emailEdit);
|
||||
// addListeners(passwordEdit);
|
||||
// if (password2Edit != null) {
|
||||
// addListeners(password2Edit);
|
||||
// }
|
||||
//
|
||||
// button = (Button) ensureFindViewById(v, R.id.create_account_button, "button");
|
||||
// button.setOnClickListener(this);
|
||||
// return v;
|
||||
// }
|
||||
|
||||
// protected void onCreateAccount(View button) {
|
||||
// Logger.debug(LOG_TAG, "onCreateAccount: Asking for username/password for new account.");
|
||||
// String email = emailEdit.getText().toString();
|
||||
// String password = passwordEdit.getText().toString();
|
||||
// activity.signUp(email, password);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
// switch (v.getId()) {
|
||||
// case R.id.create_account_button:
|
||||
// if (!validate(false)) {
|
||||
// return;
|
||||
// }
|
||||
// onCreateAccount(v);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected void addListeners(EditText editText) {
|
||||
// editText.addTextChangedListener(textChangedListener);
|
||||
// editText.setOnEditorActionListener(editorActionListener);
|
||||
// editText.setOnFocusChangeListener(focusChangeListener);
|
||||
// }
|
||||
//
|
||||
// protected class FocusChangeListener implements OnFocusChangeListener {
|
||||
// @Override
|
||||
// public void onFocusChange(View v, boolean hasFocus) {
|
||||
// if (hasFocus) {
|
||||
// return;
|
||||
// }
|
||||
// validate(false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected class EditorActionListener implements OnEditorActionListener {
|
||||
// @Override
|
||||
// public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// validate(false);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected class TextChangedListener implements TextWatcher {
|
||||
// @Override
|
||||
// public void afterTextChanged(Editable s) {
|
||||
// validate(true);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// // Do nothing.
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// // Do nothing.
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Show or hide error messaging.
|
||||
// *
|
||||
// * @param removeOnly
|
||||
// * if true, possibly remove existing error messages but do not set an
|
||||
// * error message if one was not present.
|
||||
// * @param errorResourceId
|
||||
// * of error string, or -1 to hide.
|
||||
// * @param errorView
|
||||
// * <code>TextView</code> instance to display error message in.
|
||||
// * @param edits
|
||||
// * <code>EditText</code> instances to style.
|
||||
// */
|
||||
// protected void setError(boolean removeOnly, int errorResourceId, TextView errorView, EditText... edits) {
|
||||
// if (removeOnly && errorResourceId != -1) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// int res = errorResourceId == -1 ? R.drawable.fxaccount_textfield_background : R.drawable.fxaccount_textfield_error_background;
|
||||
// for (EditText edit : edits) {
|
||||
// if (edit == null) {
|
||||
// continue;
|
||||
// }
|
||||
// edit.setBackgroundResource(res);
|
||||
// }
|
||||
// if (errorResourceId == -1) {
|
||||
// errorView.setVisibility(View.GONE);
|
||||
// errorView.setText(null);
|
||||
// } else {
|
||||
// errorView.setText(errorResourceId);
|
||||
// errorView.setVisibility(View.VISIBLE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected boolean validate(boolean removeOnly) {
|
||||
// boolean enabled = true;
|
||||
// final String email = emailEdit.getText().toString();
|
||||
// final String password = passwordEdit.getText().toString();
|
||||
//
|
||||
// if (email.length() == 0 || Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
// setError(removeOnly, -1, emailError, emailEdit);
|
||||
// } else {
|
||||
// enabled = false;
|
||||
// setError(removeOnly, R.string.fxaccount_bad_email, emailError, emailEdit);
|
||||
// }
|
||||
//
|
||||
// if (password2Edit != null) {
|
||||
// final String password2 = password2Edit.getText().toString();
|
||||
// enabled = enabled && password2.length() > 0;
|
||||
//
|
||||
// boolean passwordsMatch = password.equals(password2);
|
||||
// if (passwordsMatch) {
|
||||
// setError(removeOnly, -1, passwordError, passwordEdit, password2Edit);
|
||||
// } else {
|
||||
// enabled = false;
|
||||
// setError(removeOnly, R.string.fxaccount_bad_passwords, passwordError, passwordEdit, password2Edit);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (enabled != button.isEnabled()) {
|
||||
// Logger.debug(LOG_TAG, (enabled ? "En" : "Dis") + "abling button.");
|
||||
// button.setEnabled(enabled);
|
||||
// }
|
||||
//
|
||||
// return enabled;
|
||||
// }
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Activity which displays sign up/sign in screen to the user.
|
||||
*/
|
||||
public class FxAccountCreateSuccessActivity extends Activity {
|
||||
protected static final String LOG_TAG = FxAccountCreateSuccessActivity.class.getSimpleName();
|
||||
|
||||
protected TextView emailText;
|
||||
|
||||
/**
|
||||
* Helper to find view or error if it is missing.
|
||||
*
|
||||
* @param id of view to find.
|
||||
* @param description to print in error.
|
||||
* @return non-null <code>View</code> instance.
|
||||
*/
|
||||
public View ensureFindViewById(View v, int id, String description) {
|
||||
View view;
|
||||
if (v != null) {
|
||||
view = v.findViewById(id);
|
||||
} else {
|
||||
view = findViewById(id);
|
||||
}
|
||||
if (view == null) {
|
||||
String message = "Could not find view " + description + ".";
|
||||
Logger.error(LOG_TAG, message);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_create_success);
|
||||
|
||||
emailText = (TextView) ensureFindViewById(null, R.id.email, "email text");
|
||||
if (getIntent() != null && getIntent().getExtras() != null) {
|
||||
emailText.setText(getIntent().getStringExtra("email"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Activity which displays sign up/sign in screen to the user.
|
||||
*/
|
||||
public class FxAccountGetStartedActivity extends FxAccountAbstractActivity {
|
||||
protected static final String LOG_TAG = FxAccountGetStartedActivity.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_get_started);
|
||||
|
||||
linkifyTextViews(null, new int[] { R.id.old_firefox });
|
||||
|
||||
launchActivityOnClick(ensureFindViewById(null, R.id.get_started_button, "get started button"), FxAccountCreateAccountActivity.class);
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Activity which displays login screen to the user.
|
||||
*/
|
||||
public class FxAccountSetupActivity extends Activity {
|
||||
protected static final String LOG_TAG = FxAccountSetupActivity.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_setup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
Logger.debug(LOG_TAG, "onResume()");
|
||||
|
||||
super.onResume();
|
||||
|
||||
// Start Fennec at about:accounts page.
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
|
||||
AppConstants.ANDROID_PACKAGE_NAME + ".App");
|
||||
intent.setData(Uri.parse("about:accounts"));
|
||||
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
171
mobile/android/base/fxa/activities/FxAccountSetupTask.java
Normal file
@ -0,0 +1,171 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.InnerRequestDelegate;
|
||||
import org.mozilla.gecko.sync.HTTPFailureException;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
|
||||
/**
|
||||
* An <code>AsyncTask</code> wrapper around signing up for, and signing in to, a
|
||||
* Firefox Account.
|
||||
* <p>
|
||||
* It's strange to add explicit blocking to callback-threading code, but we do
|
||||
* it here to take advantage of Android's built in support for background work.
|
||||
* We really want to avoid making a threading mistake that brings down the whole
|
||||
* process.
|
||||
*/
|
||||
abstract class FxAccountSetupTask<T> extends AsyncTask<Void, Void, InnerRequestDelegate<T>> {
|
||||
protected static final String LOG_TAG = FxAccountSetupTask.class.getSimpleName();
|
||||
|
||||
protected final Context context;
|
||||
protected final String email;
|
||||
protected final byte[] emailUTF8;
|
||||
protected final String password;
|
||||
protected final byte[] quickStretchedPW;
|
||||
protected final FxAccountClient20 client;
|
||||
|
||||
protected ProgressDialog progressDialog = null;
|
||||
|
||||
// AsyncTask's are one-time-use, so final members are fine.
|
||||
protected final CountDownLatch latch = new CountDownLatch(1);
|
||||
protected final InnerRequestDelegate<T> innerDelegate = new InnerRequestDelegate<T>(latch);
|
||||
|
||||
protected final RequestDelegate<T> delegate;
|
||||
|
||||
public FxAccountSetupTask(Context context, String email, String password, FxAccountClient20 client, RequestDelegate<T> delegate) throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
this.context = context;
|
||||
this.email = email;
|
||||
this.emailUTF8 = email.getBytes("UTF-8");
|
||||
this.password = password;
|
||||
this.quickStretchedPW = FxAccountUtils.generateQuickStretchedPW(emailUTF8, password.getBytes("UTF-8"));
|
||||
this.client = client;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = new ProgressDialog(context);
|
||||
progressDialog.setTitle("Firefox Account..."); // XXX.
|
||||
progressDialog.setMessage("Please wait.");
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(InnerRequestDelegate<T> result) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
// We are on the UI thread, and need to invoke these callbacks here to allow UI updating.
|
||||
if (result.response != null) {
|
||||
delegate.handleSuccess(result.response);
|
||||
} else if (result.exception instanceof HTTPFailureException) {
|
||||
HTTPFailureException e = (HTTPFailureException) result.exception;
|
||||
delegate.handleFailure(e.response.getStatusCode(), e.response.httpResponse());
|
||||
} else if (innerDelegate.exception != null) {
|
||||
delegate.handleError(innerDelegate.exception);
|
||||
} else {
|
||||
delegate.handleError(new IllegalStateException("Got bad state."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(InnerRequestDelegate<T> result) {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
delegate.handleError(new IllegalStateException("Task was cancelled."));
|
||||
}
|
||||
|
||||
protected static class InnerRequestDelegate<T> implements RequestDelegate<T> {
|
||||
protected final CountDownLatch latch;
|
||||
public T response = null;
|
||||
public Exception exception = null;
|
||||
|
||||
protected InnerRequestDelegate(CountDownLatch latch) {
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
Logger.error(LOG_TAG, "Got exception.");
|
||||
this.exception = e;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(int status, HttpResponse response) {
|
||||
Logger.warn(LOG_TAG, "Got failure.");
|
||||
this.exception = new HTTPFailureException(new SyncStorageResponse(response));
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(T result) {
|
||||
Logger.info(LOG_TAG, "Got success.");
|
||||
this.response = result;
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
public static class FxAccountSignUpTask extends FxAccountSetupTask<String> {
|
||||
protected static final String LOG_TAG = FxAccountSignUpTask.class.getSimpleName();
|
||||
|
||||
public FxAccountSignUpTask(Context context, String email, String password, FxAccountClient20 client, RequestDelegate<String> delegate) throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
super(context, email, password, client, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InnerRequestDelegate<String> doInBackground(Void... arg0) {
|
||||
try {
|
||||
client.createAccount(emailUTF8, quickStretchedPW, false, innerDelegate);
|
||||
latch.await();
|
||||
return innerDelegate;
|
||||
} catch (InterruptedException e) {
|
||||
Logger.error(LOG_TAG, "Got exception logging in.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FxAccountSignInTask extends FxAccountSetupTask<LoginResponse> {
|
||||
protected static final String LOG_TAG = FxAccountSignUpTask.class.getSimpleName();
|
||||
|
||||
public FxAccountSignInTask(Context context, String email, String password, FxAccountClient20 client, RequestDelegate<LoginResponse> delegate) throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
super(context, email, password, client, delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InnerRequestDelegate<LoginResponse> doInBackground(Void... arg0) {
|
||||
try {
|
||||
client.loginAndGetKeys(emailUTF8, quickStretchedPW, innerDelegate);
|
||||
latch.await();
|
||||
return innerDelegate;
|
||||
} catch (InterruptedException e) {
|
||||
Logger.error(LOG_TAG, "Got exception signing in.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
149
mobile/android/base/fxa/activities/FxAccountSignInActivity.java
Normal file
@ -0,0 +1,149 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator;
|
||||
import org.mozilla.gecko.sync.HTTPFailureException;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
|
||||
/**
|
||||
* Activity which displays sign in screen to the user.
|
||||
*/
|
||||
public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
|
||||
protected static final String LOG_TAG = FxAccountSignInActivity.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_sign_in);
|
||||
|
||||
localErrorTextView = (TextView) ensureFindViewById(null, R.id.local_error, "local error text view");
|
||||
emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
|
||||
passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit");
|
||||
showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button");
|
||||
button = (Button) ensureFindViewById(null, R.id.sign_in_button, "sign in button");
|
||||
|
||||
minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in.
|
||||
createSignInButton();
|
||||
addListeners();
|
||||
updateButtonState();
|
||||
createShowPasswordButton();
|
||||
|
||||
this.launchActivityOnClick(ensureFindViewById(null, R.id.create_account_link, "create account instead link"), FxAccountCreateAccountActivity.class);
|
||||
// Not yet implemented.
|
||||
this.launchActivityOnClick(ensureFindViewById(null, R.id.forgot_password_link, "forgot password link"), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (FxAccountAuthenticator.getFirefoxAccounts(this).length > 0) {
|
||||
redirectToActivity(FxAccountStatusActivity.class);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected class SignInDelegate implements RequestDelegate<LoginResponse> {
|
||||
public final String email;
|
||||
public final String password;
|
||||
public final String serverURI;
|
||||
|
||||
public SignInDelegate(String email, String password, String serverURI) {
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this.serverURI = serverURI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
showRemoteError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(int status, HttpResponse response) {
|
||||
showRemoteError(new HTTPFailureException(new SyncStorageResponse(response)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(LoginResponse result) {
|
||||
Activity activity = FxAccountSignInActivity.this;
|
||||
Logger.info(LOG_TAG, "Got success signing in.");
|
||||
|
||||
// We're on the UI thread, but it's okay to create the account here.
|
||||
Account account;
|
||||
try {
|
||||
account = AndroidFxAccount.addAndroidAccount(activity, email, password,
|
||||
serverURI, result.sessionToken, result.keyFetchToken, result.verified);
|
||||
if (account == null) {
|
||||
throw new RuntimeException("XXX what?");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// For great debugging.
|
||||
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
|
||||
new AndroidFxAccount(activity, account).dump();
|
||||
}
|
||||
|
||||
Toast.makeText(getApplicationContext(), "Got success creating account.", Toast.LENGTH_LONG).show();
|
||||
redirectToActivity(FxAccountStatusActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void signIn(String email, String password) {
|
||||
String serverURI = FxAccountConstants.DEFAULT_IDP_ENDPOINT;
|
||||
RequestDelegate<LoginResponse> delegate = new SignInDelegate(email, password, serverURI);
|
||||
Executor executor = Executors.newSingleThreadExecutor();
|
||||
FxAccountClient20 client = new FxAccountClient20(serverURI, executor);
|
||||
try {
|
||||
new FxAccountSignInTask(this, email, password, client, delegate).execute();
|
||||
} catch (Exception e) {
|
||||
showRemoteError(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createSignInButton() {
|
||||
button.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final String email = emailEdit.getText().toString();
|
||||
final String password = passwordEdit.getText().toString();
|
||||
signIn(email, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
150
mobile/android/base/fxa/activities/FxAccountStatusActivity.java
Normal file
@ -0,0 +1,150 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.activities;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||
import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Activity which displays account status.
|
||||
*/
|
||||
public class FxAccountStatusActivity extends FxAccountAbstractActivity {
|
||||
protected static final String LOG_TAG = FxAccountStatusActivity.class.getSimpleName();
|
||||
|
||||
protected View connectionStatusUnverifiedView;
|
||||
protected View connectionStatusSignInView;
|
||||
protected View connectionStatusSyncingView;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
|
||||
|
||||
super.onCreate(icicle);
|
||||
setContentView(R.layout.fxaccount_status);
|
||||
|
||||
connectionStatusUnverifiedView = ensureFindViewById(null, R.id.unverified_view, "unverified view");
|
||||
connectionStatusSignInView = ensureFindViewById(null, R.id.sign_in_view, "sign in view");
|
||||
connectionStatusSyncingView = ensureFindViewById(null, R.id.syncing_view, "syncing view");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected void refresh(Account account) {
|
||||
TextView email = (TextView) findViewById(R.id.email);
|
||||
|
||||
if (account == null) {
|
||||
redirectToActivity(FxAccountGetStartedActivity.class);
|
||||
return;
|
||||
}
|
||||
|
||||
AndroidFxAccount fxAccount = new AndroidFxAccount(this, account);
|
||||
|
||||
email.setText(account.name);
|
||||
|
||||
// Not as good as interrogating state machine, but will do for now.
|
||||
if (!fxAccount.isVerified()) {
|
||||
connectionStatusUnverifiedView.setVisibility(View.VISIBLE);
|
||||
connectionStatusSignInView.setVisibility(View.GONE);
|
||||
connectionStatusSyncingView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fxAccount.getQuickStretchedPW() == null) {
|
||||
connectionStatusUnverifiedView.setVisibility(View.GONE);
|
||||
connectionStatusSignInView.setVisibility(View.VISIBLE);
|
||||
connectionStatusSyncingView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
connectionStatusUnverifiedView.setVisibility(View.GONE);
|
||||
connectionStatusSignInView.setVisibility(View.GONE);
|
||||
connectionStatusSyncingView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
protected void refresh() {
|
||||
Account accounts[] = FxAccountAuthenticator.getFirefoxAccounts(this);
|
||||
if (accounts.length < 1) {
|
||||
refresh(null);
|
||||
return;
|
||||
}
|
||||
refresh(accounts[0]);
|
||||
}
|
||||
|
||||
protected void dumpAccountDetails() {
|
||||
Account accounts[] = FxAccountAuthenticator.getFirefoxAccounts(this);
|
||||
if (accounts.length < 1) {
|
||||
return;
|
||||
}
|
||||
AndroidFxAccount fxAccount = new AndroidFxAccount(this, accounts[0]);
|
||||
fxAccount.dump();
|
||||
}
|
||||
|
||||
protected void forgetAccountTokens() {
|
||||
Account accounts[] = FxAccountAuthenticator.getFirefoxAccounts(this);
|
||||
if (accounts.length < 1) {
|
||||
return;
|
||||
}
|
||||
AndroidFxAccount fxAccount = new AndroidFxAccount(this, accounts[0]);
|
||||
fxAccount.forgetAccountTokens();
|
||||
fxAccount.dump();
|
||||
}
|
||||
|
||||
protected void forgetQuickStretchedPW() {
|
||||
Account accounts[] = FxAccountAuthenticator.getFirefoxAccounts(this);
|
||||
if (accounts.length < 1) {
|
||||
return;
|
||||
}
|
||||
AndroidFxAccount fxAccount = new AndroidFxAccount(this, accounts[0]);
|
||||
fxAccount.forgetQuickstretchedPW();
|
||||
fxAccount.dump();
|
||||
}
|
||||
|
||||
public void onClickRefresh(View view) {
|
||||
Logger.debug(LOG_TAG, "Refreshing.");
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void onClickForgetAccountTokens(View view) {
|
||||
Logger.debug(LOG_TAG, "Forgetting account tokens.");
|
||||
forgetAccountTokens();
|
||||
}
|
||||
|
||||
public void onClickForgetPassword(View view) {
|
||||
Logger.debug(LOG_TAG, "Forgetting quickStretchedPW.");
|
||||
forgetQuickStretchedPW();
|
||||
}
|
||||
|
||||
public void onClickDumpAccountDetails(View view) {
|
||||
Logger.debug(LOG_TAG, "Dumping account details.");
|
||||
dumpAccountDetails();
|
||||
}
|
||||
|
||||
public void onClickGetStarted(View view) {
|
||||
Logger.debug(LOG_TAG, "Launching get started activity.");
|
||||
redirectToActivity(FxAccountGetStartedActivity.class);
|
||||
}
|
||||
|
||||
public void onClickVerify(View view) {
|
||||
Logger.debug(LOG_TAG, "Launching verification activity.");
|
||||
}
|
||||
|
||||
public void onClickSignIn(View view) {
|
||||
Logger.debug(LOG_TAG, "Launching sign in again activity.");
|
||||
}
|
||||
}
|
@ -83,7 +83,8 @@ public class AndroidFxAccount implements AbstractFxAccount {
|
||||
|
||||
@Override
|
||||
public byte[] getQuickStretchedPW() {
|
||||
return Utils.hex2Byte(accountManager.getPassword(account));
|
||||
String quickStretchedPW = accountManager.getPassword(account);
|
||||
return quickStretchedPW == null ? null : Utils.hex2Byte(quickStretchedPW);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -305,8 +306,15 @@ public class AndroidFxAccount implements AbstractFxAccount {
|
||||
/**
|
||||
* <b>For debugging only!</b>
|
||||
*/
|
||||
public void resetAccountTokens() {
|
||||
public void forgetAccountTokens() {
|
||||
accountManager.setUserData(account, ACCOUNT_KEY_SESSION_TOKEN, null);
|
||||
accountManager.setUserData(account, ACCOUNT_KEY_KEY_FETCH_TOKEN, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>For debugging only!</b>
|
||||
*/
|
||||
public void forgetQuickstretchedPW() {
|
||||
accountManager.setPassword(account, null);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ package org.mozilla.gecko.fxa.authenticator;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountSetupActivity;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
@ -81,7 +81,7 @@ public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
return res;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, FxAccountSetupActivity.class);
|
||||
Intent intent = new Intent(context, FxAccountGetStartedActivity.class);
|
||||
res.putParcelable(AccountManager.KEY_INTENT, intent);
|
||||
return res;
|
||||
}
|
||||
|
@ -1,175 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.fxa.sync;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountClient10;
|
||||
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
|
||||
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
||||
import org.mozilla.gecko.fxa.authenticator.FxAccountAuthenticator;
|
||||
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||
import org.mozilla.gecko.sync.HTTPFailureException;
|
||||
import org.mozilla.gecko.sync.net.AuthHeaderProvider;
|
||||
import org.mozilla.gecko.sync.net.HawkAuthHeaderProvider;
|
||||
import org.mozilla.gecko.sync.net.SyncStorageResponse;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
|
||||
import org.mozilla.gecko.tokenserver.TokenServerClient;
|
||||
import org.mozilla.gecko.tokenserver.TokenServerClientDelegate;
|
||||
import org.mozilla.gecko.tokenserver.TokenServerException;
|
||||
import org.mozilla.gecko.tokenserver.TokenServerToken;
|
||||
|
||||
import android.content.Context;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
|
||||
/**
|
||||
* Represent a Firefox Account.
|
||||
*
|
||||
* This is the FxAccounts equivalent of {@link SyncAccountParameters}.
|
||||
*/
|
||||
public class FxAccount {
|
||||
protected static final String LOG_TAG = FxAccount.class.getSimpleName();
|
||||
|
||||
public interface Delegate {
|
||||
public void handleSuccess(String uid, String endpoint, AuthHeaderProvider authHeaderProvider);
|
||||
public void handleError(Exception e);
|
||||
}
|
||||
|
||||
protected final String email;
|
||||
protected final byte[] sessionTokenBytes;
|
||||
protected final byte[] kA;
|
||||
protected final byte[] kB;
|
||||
protected final String idpEndpoint;
|
||||
protected final String authEndpoint;
|
||||
protected final Executor executor;
|
||||
|
||||
public FxAccount(String email, byte[] sessionTokenBytes, byte[] kA, byte[] kB, String idpEndpoint, String authEndpoint) {
|
||||
this.email = email;
|
||||
this.sessionTokenBytes = sessionTokenBytes;
|
||||
this.kA = kA;
|
||||
this.kB = kB;
|
||||
this.idpEndpoint = idpEndpoint;
|
||||
this.authEndpoint = authEndpoint;
|
||||
this.executor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
protected static class InnerFxAccountClientRequestDelegate implements FxAccountClient10.RequestDelegate<String> {
|
||||
protected final Executor executor;
|
||||
protected final String audience;
|
||||
protected final String tokenServerEndpoint;
|
||||
protected final BrowserIDKeyPair keyPair;
|
||||
protected final Delegate delegate;
|
||||
|
||||
protected InnerFxAccountClientRequestDelegate(Executor executor, String audience, String tokenServerEndpoint, BrowserIDKeyPair keyPair, Delegate delegate) {
|
||||
this.executor = executor;
|
||||
this.audience = audience;
|
||||
this.tokenServerEndpoint = tokenServerEndpoint;
|
||||
this.keyPair = keyPair;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
Logger.error(LOG_TAG, "Failed to sign.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(int status, HttpResponse response) {
|
||||
HTTPFailureException e = new HTTPFailureException(new SyncStorageResponse(response));
|
||||
Logger.error(LOG_TAG, "Failed to sign.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(String certificate) {
|
||||
Logger.pii(LOG_TAG, "Got certificate " + certificate);
|
||||
|
||||
try {
|
||||
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience);
|
||||
if (Logger.LOG_PERSONAL_INFORMATION) {
|
||||
Logger.pii(LOG_TAG, "Generated assertion " + assertion);
|
||||
JSONWebTokenUtils.dumpAssertion(assertion);
|
||||
}
|
||||
|
||||
TokenServerClient tokenServerclient = new TokenServerClient(new URI(tokenServerEndpoint), executor);
|
||||
|
||||
tokenServerclient.getTokenFromBrowserIDAssertion(assertion, true, new InnerTokenServerClientDelegate(delegate));
|
||||
} catch (Exception e) {
|
||||
Logger.error(LOG_TAG, "Got error doing stuff.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static class InnerTokenServerClientDelegate implements TokenServerClientDelegate {
|
||||
protected final Delegate delegate;
|
||||
|
||||
public InnerTokenServerClientDelegate(Delegate delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(TokenServerToken token) {
|
||||
AuthHeaderProvider authHeaderProvider;
|
||||
try {
|
||||
authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Logger.error(LOG_TAG, "Failed to sync.", e);
|
||||
delegate.handleError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
delegate.handleSuccess(token.uid, token.endpoint, authHeaderProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFailure(TokenServerException e) {
|
||||
Logger.error(LOG_TAG, "Failed fetching server token.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
Logger.error(LOG_TAG, "Got error fetching token server token.", e);
|
||||
delegate.handleError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a signed certificate and exchange it for a token.
|
||||
*
|
||||
* This is temporary code that does not do production-ready caching. This
|
||||
* should be made obsolete by a fully featured {@link FxAccountAuthenticator}.
|
||||
*
|
||||
* @param context to use.
|
||||
* @param tokenServerEndpoint to get token from.
|
||||
* @param keyPair to sign certificate for.
|
||||
* @param delegate to callback to.
|
||||
*/
|
||||
public void login(final Context context, final String tokenServerEndpoint,
|
||||
final BrowserIDKeyPair keyPair, final Delegate delegate) {
|
||||
ExtendedJSONObject publicKeyObject;
|
||||
try {
|
||||
publicKeyObject = keyPair.getPublic().toJSONObject();
|
||||
} catch (Exception e) {
|
||||
delegate.handleError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// We have nested executors in play here. Since we control the executor and
|
||||
// the delegates, we can guarantee that there is no dead-lock between the
|
||||
// inner FxAccountClient delegate, the outer TokenServerClient delegate, and
|
||||
// the user supplied delegate.
|
||||
FxAccountClient10 fxAccountClient = new FxAccountClient10(idpEndpoint, executor);
|
||||
fxAccountClient.sign(sessionTokenBytes, publicKeyObject,
|
||||
JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS,
|
||||
new InnerFxAccountClientRequestDelegate(executor, authEndpoint, tokenServerEndpoint, keyPair, delegate));
|
||||
}
|
||||
}
|
@ -57,12 +57,17 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
*/
|
||||
protected static class SessionCallback implements BaseGlobalSessionCallback {
|
||||
protected final CountDownLatch latch;
|
||||
protected final SyncResult syncResult;
|
||||
|
||||
public SessionCallback(CountDownLatch latch) {
|
||||
public SessionCallback(CountDownLatch latch, SyncResult syncResult) {
|
||||
if (latch == null) {
|
||||
throw new IllegalArgumentException("latch must not be null");
|
||||
}
|
||||
if (syncResult == null) {
|
||||
throw new IllegalArgumentException("syncResult must not be null");
|
||||
}
|
||||
this.latch = latch;
|
||||
this.syncResult = syncResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,20 +91,47 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
public void handleStageCompleted(Stage currentState, GlobalSession globalSession) {
|
||||
}
|
||||
|
||||
/**
|
||||
* No error! Say that we made progress.
|
||||
*/
|
||||
protected void setSyncResultSuccess() {
|
||||
syncResult.stats.numUpdates += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft error. Say that we made progress, so that Android will sync us again
|
||||
* after exponential backoff.
|
||||
*/
|
||||
protected void setSyncResultSoftError() {
|
||||
syncResult.stats.numUpdates += 1;
|
||||
syncResult.stats.numIoExceptions += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hard error. We don't want Android to sync us again, even if we make
|
||||
* progress, until the user intervenes.
|
||||
*/
|
||||
protected void setSyncResultHardError() {
|
||||
syncResult.stats.numAuthExceptions += 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSuccess(GlobalSession globalSession) {
|
||||
Logger.info(LOG_TAG, "Successfully synced!");
|
||||
setSyncResultSuccess();
|
||||
Logger.info(LOG_TAG, "Sync succeeded.");
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(GlobalSession globalSession, Exception ex) {
|
||||
Logger.warn(LOG_TAG, "Sync failed.", ex);
|
||||
public void handleError(GlobalSession globalSession, Exception e) {
|
||||
setSyncResultSoftError();
|
||||
Logger.warn(LOG_TAG, "Sync failed.", e);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAborted(GlobalSession globalSession, String reason) {
|
||||
setSyncResultSoftError();
|
||||
Logger.warn(LOG_TAG, "Sync aborted: " + reason);
|
||||
latch.countDown();
|
||||
}
|
||||
@ -122,6 +154,9 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
" with instance " + this + ".");
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final BaseGlobalSessionCallback callback = new SessionCallback(latch, syncResult);
|
||||
|
||||
|
||||
try {
|
||||
final String authEndpoint = FxAccountConstants.DEFAULT_AUTH_ENDPOINT;
|
||||
final String tokenServerEndpoint = authEndpoint + (authEndpoint.endsWith("/") ? "" : "/") + "1.0/sync/1.1";
|
||||
@ -150,7 +185,6 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
FxAccountConstants.pii(LOG_TAG, "Got token! uid is " + token.uid + " and endpoint is " + token.endpoint + ".");
|
||||
sharedPrefs.edit().putLong("tokenFailures", 0).commit();
|
||||
|
||||
final BaseGlobalSessionCallback callback = new SessionCallback(latch);
|
||||
FxAccountGlobalSession globalSession = null;
|
||||
try {
|
||||
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
||||
@ -187,7 +221,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
@Override
|
||||
public void handleError(Exception e) {
|
||||
Logger.error(LOG_TAG, "Failed to get token.", e);
|
||||
latch.countDown();
|
||||
callback.handleError(null, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -195,15 +229,17 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
@Override
|
||||
public void handleError(FxAccountLoginException e) {
|
||||
Logger.error(LOG_TAG, "Got error logging in.", e);
|
||||
latch.countDown();
|
||||
callback.handleError(null, e);
|
||||
}
|
||||
});
|
||||
|
||||
latch.await();
|
||||
} catch (Exception e) {
|
||||
Logger.error(LOG_TAG, "Got error syncing.", e);
|
||||
latch.countDown();
|
||||
callback.handleError(null, e);
|
||||
}
|
||||
|
||||
Logger.error(LOG_TAG, "Syncing done.");
|
||||
}
|
||||
|
||||
protected void debugAssertion(String audience, String assertion) {
|
||||
|
@ -108,4 +108,52 @@
|
||||
<!ENTITY sync.text.tab.not.sent.label 'There was a problem sending your tab.'>
|
||||
|
||||
<!-- Firefox Account strings -->
|
||||
<!ENTITY firefox.accounts 'Firefox Accounts'>
|
||||
<!ENTITY fxaccount.label '&fxaccountBrand.fullName.label;'>
|
||||
<!ENTITY fxaccount.policy 'By proceeding you agree with the <a
|
||||
href="http://mozilla.com">Terms of Service</a> and <a
|
||||
href="http://mozilla.com">Privacy Policy</a>.'>
|
||||
<!ENTITY fxaccount.forgot.password 'Forgot password?'>
|
||||
<!ENTITY fxaccount.create.account.button.label 'Create Account'>
|
||||
<!ENTITY fxaccount.sign.in.button.label 'Sign In'>
|
||||
<!ENTITY fxaccount.email.hint 'Email'>
|
||||
<!ENTITY fxaccount.password.hint 'Password'>
|
||||
<!ENTITY fxaccount.password2.hint 'Confirm password'>
|
||||
<!ENTITY fxaccount.bad.email 'We need a real email address.'>
|
||||
<!ENTITY fxaccount.bad.passwords 'Your passwords must match.'>
|
||||
<!ENTITY fxaccount.sign.in.instead 'Already have an account? Sign in!'>
|
||||
<!ENTITY fxaccount.sign.up.instead 'Don\'t have an account? Sign up instead.'>
|
||||
|
||||
<!ENTITY fxaccount.sign.in 'Sign in'>
|
||||
<!ENTITY fxaccount.create.account 'Create an account'>
|
||||
|
||||
<!ENTITY fxaccount.old.firefox '<a href="http://mozilla.com">Using &syncBrand.shortName.label; with an older version of &brandShortName;?</a>'>
|
||||
|
||||
<!ENTITY fxaccount.when.were.you.born 'When were you born?'>
|
||||
<!ENTITY fxaccount.year.of.birth 'Year of birth'>
|
||||
|
||||
<!ENTITY fxaccount.icon.contentDescription 'Firefox Accounts icon'>
|
||||
<!ENTITY fxaccount.password.hide 'Hide'>
|
||||
<!ENTITY fxaccount.password.show 'Show'>
|
||||
<!ENTITY fxaccount.get.started 'Get started'>
|
||||
<!ENTITY fxaccount.description 'Sign in to backup and sync all your settings and bookmarks!'>
|
||||
<!ENTITY fxaccount.sign.up 'Sign up'>
|
||||
|
||||
<!-- Android > Accounts > [Firefox Account] Screen -->
|
||||
<!ENTITY fxaccount.options.title 'Options'>
|
||||
<!ENTITY fxaccount.options.configure.title 'Configure your Firefox Account'>
|
||||
<!ENTITY fxaccount.intro.contentDescription 'Firefox Accounts introduction graphics'>
|
||||
<!ENTITY fxaccount.confirmation.email.sent 'Confirmation email sent!'>
|
||||
<!ENTITY fxaccount.mail.contentDescription 'Firefox Accounts envelope graphic'>
|
||||
<!ENTITY fxaccount.confirmation.description 'Your confirmation link awaits at:'>
|
||||
<!ENTITY fxaccount.offer.resend.confirmation.email 'Don\'t see an email? Resend.'>
|
||||
<!ENTITY fxaccount.password.length.restriction 'Must be at least 8 characters.'>
|
||||
<!ENTITY fxaccount.change.password 'Change password'>
|
||||
<!ENTITY fxaccount.status.needs.verification 'Your account needs to be verified. Tap here to verify and start syncing.'>
|
||||
<!ENTITY fxaccount.status.needs.credentials 'Your account details are out of date. Tap here to sign in again and resume syncing.'>
|
||||
<!ENTITY fxaccount.status.syncing 'Firefox is syncing...'>
|
||||
<!ENTITY fxaccount.status.sync 'Sync' >
|
||||
<!ENTITY fxaccount.status.bookmarks 'Bookmarks'>
|
||||
<!ENTITY fxaccount.status.history 'History'>
|
||||
<!ENTITY fxaccount.status.passwords 'Passwords'>
|
||||
<!ENTITY fxaccount.status.tabs 'Open tabs'>
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/fxaccount_checkbox.png
Executable file
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/fxaccount_icon.png
Executable file
After Width: | Height: | Size: 4.0 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/fxaccount_intro.png
Executable file
After Width: | Height: | Size: 10 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/fxaccount_mail.png
Executable file
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/graphic_mail.png
Executable file
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 603 B |
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/fxaccount_button_background_pressed" />
|
||||
<item android:state_enabled="false"
|
||||
android:drawable="@drawable/fxaccount_button_background_disabled" />
|
||||
<item android:state_enabled="true"
|
||||
android:drawable="@drawable/fxaccount_button_background_enabled" />
|
||||
</selector>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
<solid android:color="@color/fxaccount_button_background_inactive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
<solid android:color="@color/fxaccount_button_background_active" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
<solid android:color="@color/fxaccount_button_background_hit" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/fxaccount_button_textColor" />
|
||||
</selector>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="@color/fxaccount_link_textColor_pressed" />
|
||||
<item android:color="@color/fxaccount_link_textColor" />
|
||||
</selector>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
</shape>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_focused="false"
|
||||
android:drawable="@drawable/fxaccount_password_inactive" />
|
||||
<item
|
||||
android:drawable="@drawable/fxaccount_password_active" />
|
||||
</selector>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_focused="false"
|
||||
android:drawable="@drawable/fxaccount_password_button_inactive" />
|
||||
<item
|
||||
android:drawable="@drawable/fxaccount_password_button_active" />
|
||||
</selector>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderInactive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderInactive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius"
|
||||
android:topLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:topRightRadius="0dp"
|
||||
android:bottomLeftRadius="@dimen/fxaccount_corner_radius"
|
||||
android:bottomRightRadius="0dp" />
|
||||
</shape>
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid
|
||||
android:color="@android:color/transparent" />
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderActive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:state_focused="false"
|
||||
android:drawable="@drawable/fxaccount_textfield_inactive" />
|
||||
<item
|
||||
android:drawable="@drawable/fxaccount_textfield_active" />
|
||||
</selector>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="@dimen/fxaccount_stroke_width"
|
||||
android:color="@color/fxaccount_input_borderInactive" />
|
||||
<corners
|
||||
android:radius="@dimen/fxaccount_corner_radius" />
|
||||
</shape>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle" >
|
||||
<solid android:color="@color/fxaccount_error" />
|
||||
</shape>
|
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/create_account_view"
|
||||
style="@style/FxAccountMiddle" >
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:text="@string/firefox_accounts" />
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountSubHeaderItem"
|
||||
android:text="@string/fxaccount_create_account" />
|
||||
|
||||
<include layout="@layout/fxaccount_email_password_view" />
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountLinkifiedItem"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/fxaccount_password_length_restriction" />
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@string/fxaccount_when_were_you_born"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<!-- Per http://stackoverflow.com/questions/2359176/android-edittext-onclicklistener, not allowing focus allows us to highjack the click. -->
|
||||
|
||||
<EditText
|
||||
android:id="@+id/year_edit"
|
||||
style="@style/FxAccountEditItem"
|
||||
android:layout_marginTop="15dp"
|
||||
android:drawableRight="@drawable/fxaccount_ddarrow_inactive"
|
||||
android:focusable="false"
|
||||
android:hint="@string/fxaccount_year_of_birth"
|
||||
android:inputType="none" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_error"
|
||||
style="@style/FxAccountErrorItem" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/create_account_button"
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginTop="45dp"
|
||||
android:text="@string/fxaccount_create_account_button_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sign_in_instead_link"
|
||||
style="@style/FxAccountLinkItem"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:focusable="true"
|
||||
android:text="@string/fxaccount_sign_in_instead" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/policy"
|
||||
style="@style/FxAccountLinkifiedItem"
|
||||
android:text="@string/fxaccount_policy" />
|
||||
|
||||
<LinearLayout style="@style/FxAccountSpacer" />
|
||||
|
||||
<ImageView
|
||||
style="@style/FxAccountIcon"
|
||||
android:contentDescription="@string/fxaccount_icon_contentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/create_success_view"
|
||||
style="@style/FxAccountMiddle" >
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:text="@string/firefox_accounts" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountSubHeaderItem"
|
||||
android:text="@string/fxaccount_confirmation_email_sent" >
|
||||
</TextView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="45dp"
|
||||
android:contentDescription="@string/fxaccount_mail_contentDescription"
|
||||
android:src="@drawable/fxaccount_mail" >
|
||||
</ImageView>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:text="@string/fxaccount_confirmation_description"
|
||||
android:textSize="18sp" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/email"
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_marginBottom="45dp"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountLinkItem"
|
||||
android:text="@string/fxaccount_offer_resend_confirmation_email" />
|
||||
|
||||
<LinearLayout style="@style/FxAccountSpacer" />
|
||||
|
||||
<ImageView
|
||||
style="@style/FxAccountIcon"
|
||||
android:contentDescription="@string/fxaccount_icon_contentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_error"
|
||||
style="@style/FxAccountErrorItem" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/email"
|
||||
style="@style/FxAccountEditItem"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/fxaccount_email_hint"
|
||||
android:inputType="textEmailAddress" >
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
style="@style/FxAccountEditItem"
|
||||
android:background="@drawable/fxaccount_password_background"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:hint="@string/fxaccount_password_hint"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/show_password"
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="0"
|
||||
android:background="@drawable/fxaccount_password_button_background"
|
||||
android:minHeight="0dp"
|
||||
android:padding="0dp"
|
||||
android:text="@string/fxaccount_password_show"
|
||||
android:textColor="@color/fxaccount_input_textColor" >
|
||||
</Button>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/intro_view"
|
||||
style="@style/FxAccountMiddle" >
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:text="@string/firefox_accounts" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountSubHeaderItem"
|
||||
android:text="@string/fxaccount_get_started" >
|
||||
</TextView>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:contentDescription="@string/fxaccount_intro_contentDescription"
|
||||
android:src="@drawable/fxaccount_intro" >
|
||||
</ImageView>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_marginBottom="45dp"
|
||||
android:text="@string/fxaccount_description" >
|
||||
</TextView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/get_started_button"
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:onClick="onClickGetStarted"
|
||||
android:text="@string/fxaccount_get_started" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/old_firefox"
|
||||
style="@style/FxAccountLinkifiedItem"
|
||||
android:text="@string/fxaccount_old_firefox" />
|
||||
|
||||
<LinearLayout style="@style/FxAccountSpacer" />
|
||||
|
||||
<ImageView
|
||||
style="@style/FxAccountIcon"
|
||||
android:contentDescription="@string/fxaccount_icon_contentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
65
mobile/android/base/resources/layout/fxaccount_sign_in.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/sign_in_view"
|
||||
style="@style/FxAccountMiddle" >
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:text="@string/firefox_accounts" />
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountSubHeaderItem"
|
||||
android:text="@string/fxaccount_sign_in" />
|
||||
|
||||
<include layout="@layout/fxaccount_email_password_view" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/sign_in_button"
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginTop="45dp"
|
||||
android:text="@string/fxaccount_sign_in_button_label" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/forgot_password_link"
|
||||
style="@style/FxAccountLinkItem"
|
||||
android:layout_gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:gravity="left"
|
||||
android:text="@string/fxaccount_forgot_password" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/create_account_link"
|
||||
style="@style/FxAccountLinkItem"
|
||||
android:layout_gravity="right"
|
||||
android:layout_weight="1"
|
||||
android:gravity="right"
|
||||
android:text="@string/fxaccount_create_account" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/FxAccountSpacer" />
|
||||
|
||||
<ImageView
|
||||
style="@style/FxAccountIcon"
|
||||
android:contentDescription="@string/fxaccount_icon_contentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
159
mobile/android/base/resources/layout/fxaccount_status.xml
Normal file
@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/existing_user"
|
||||
style="@style/FxAccountMiddle" >
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:text="@string/firefox_accounts" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/email"
|
||||
style="@style/FxAccountSubHeaderItem" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/change_password"
|
||||
style="@style/FxAccountLinkItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_change_password" >
|
||||
</TextView>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/connection_status_view"
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unverified_view"
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="#fad4d2"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableStart="@drawable/fxaccount_sync_error"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:text="@string/fxaccount_status_needs_verification"
|
||||
android:visibility="gone" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sign_in_view"
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="#fad4d2"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableStart="@drawable/fxaccount_sync_error"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:text="@string/fxaccount_status_needs_credentials"
|
||||
android:visibility="gone" >
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/syncing_view"
|
||||
style="@style/FxAccountTextItem"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="#d1e7fe"
|
||||
android:drawablePadding="10dp"
|
||||
android:drawableStart="@drawable/fxaccount_sync_icon"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="10dp"
|
||||
android:text="@string/fxaccount_status_syncing"
|
||||
android:visibility="visible" >
|
||||
</TextView>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/FxAccountHeaderItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_status_sync" >
|
||||
</TextView>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/bookmarks_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_status_bookmarks" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/history_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_status_history" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/passwords_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_status_passwords" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/tabs_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/fxaccount_status_tabs" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/debug_buttons"
|
||||
style="@style/FxAccountMiddle"
|
||||
android:background="#7f7f7f" >
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:onClick="onClickRefresh"
|
||||
android:text="Refresh" />
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:onClick="onClickDumpAccountDetails"
|
||||
android:text="Dump Account Details" />
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:onClick="onClickForgetAccountTokens"
|
||||
android:text="Forget sessionToken and keyFetchToken" />
|
||||
|
||||
<Button
|
||||
style="@style/FxAccountButton"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:onClick="onClickForgetPassword"
|
||||
android:text="Forget password" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
27
mobile/android/base/resources/values/fxaccount_colors.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<color name="fxaccount_error_dark">#FDEBE5</color>
|
||||
<color name="fxaccount_error_light">#E24B03</color>
|
||||
|
||||
<color name="fxaccount_textColor">#3c454d</color>
|
||||
|
||||
<color name="fxaccount_linkified_textColor">#c0c9d0</color>
|
||||
<color name="fxaccount_linkified_textColorLink">#0096dd</color>
|
||||
|
||||
<color name="fxaccount_error">#d63920</color>
|
||||
<color name="fxaccount_button_textColor">#ffffff</color>
|
||||
<color name="fxaccount_button_background_active">#e66000</color>
|
||||
<color name="fxaccount_button_background_hit">#fd9500</color>
|
||||
<color name="fxaccount_button_background_loading">#424f59</color>
|
||||
<color name="fxaccount_button_background_inactive">#c0c9d0</color>
|
||||
<color name="fxaccount_input_textColor">#424f59</color>
|
||||
<color name="fxaccount_input_textColorHint">#c0c9d0</color>
|
||||
<color name="fxaccount_link_textColor">#0096dd</color>
|
||||
<color name="fxaccount_link_textColor_pressed">#00767d</color>
|
||||
<color name="fxaccount_input_borderActive">#6a7b86</color>
|
||||
<color name="fxaccount_input_borderInactive">#c0c9d0</color>
|
||||
</resources>
|
@ -3,9 +3,8 @@
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
<resources>
|
||||
<dimen name="fxaccount_stroke_width">1dp</dimen>
|
||||
<dimen name="fxaccount_corner_radius">5dp</dimen>
|
||||
<dimen name="fxaccount_input_padding">10dp</dimen>
|
||||
</resources>
|
105
mobile/android/base/resources/values/fxaccount_styles.xml
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="FxAccountTheme" parent="@style/Gecko" />
|
||||
|
||||
<style name="FxAccountMiddle">
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:paddingTop">25dp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">15dp</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountSpacer">
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">0dp</item>
|
||||
<item name="android:layout_weight">1</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountHeaderItem" parent="@style/FxAccountTextItem">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">24sp</item>
|
||||
<item name="android:layout_marginBottom">10dp</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountSubHeaderItem" parent="@style/FxAccountTextItem">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:layout_marginBottom">45dp</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountTextItem" parent="@android:style/TextAppearance.Medium">
|
||||
<item name="android:textColor">@color/fxaccount_textColor</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:gravity">center_horizontal</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountLinkItem" parent="@style/FxAccountTextItem">
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:focusable">false</item>
|
||||
<item name="android:textColor">@drawable/fxaccount_linkitem_textcolor</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountButton" parent="@android:style/Widget.Button">
|
||||
<item name="android:background">@drawable/fxaccount_button_background</item>
|
||||
<item name="android:textColor">@drawable/fxaccount_button_color</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:padding">20dp</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountEditItem" parent="@android:style/Widget.EditText">
|
||||
<item name="android:padding">@dimen/fxaccount_input_padding</item>
|
||||
<item name="android:background">@drawable/fxaccount_textfield_background</item>
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:textColor">@color/fxaccount_input_textColor</item>
|
||||
<item name="android:textColorHint">@color/fxaccount_input_textColorHint</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountLinkifiedItem" parent="@android:style/TextAppearance.Small">
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:focusable">true</item>
|
||||
<item name="android:textColor">@color/fxaccount_linkified_textColor</item>
|
||||
<item name="android:textColorLink">@color/fxaccount_linkified_textColorLink</item>
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountIcon">
|
||||
<item name="android:layout_marginTop">20dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_gravity">center_horizontal</item>
|
||||
<item name="android:src">@drawable/fxaccount_icon</item>
|
||||
</style>
|
||||
|
||||
<style name="FxAccountErrorItem">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_gravity">left</item>
|
||||
<item name="android:layout_margin">5dp</item>
|
||||
<item name="android:background">@drawable/fxaccount_textview_error_background</item>
|
||||
<item name="android:padding">5dp</item>
|
||||
<item name="android:text">Error</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:visibility">invisible</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -8,4 +8,4 @@
|
||||
android:icon="@drawable/icon"
|
||||
android:smallIcon="@drawable/icon"
|
||||
android:label="@string/fxaccount_label"
|
||||
/>
|
||||
android:accountPreferences="@xml/fxaccount_options" />
|
||||
|
18
mobile/android/base/resources/xml/fxaccount_options.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:title="@string/fxaccount_options_title" />
|
||||
<PreferenceScreen
|
||||
android:key="options"
|
||||
android:title="@string/fxaccount_options_configure_title">
|
||||
<intent
|
||||
android:action="android.intent.action.MAIN"
|
||||
android:targetPackage="@string/android_package_name_for_ui"
|
||||
android:targetClass="org.mozilla.gecko.fxa.activities.FxAccountStatusActivity">
|
||||
</intent>
|
||||
</PreferenceScreen>
|
||||
</PreferenceScreen>
|
@ -1,9 +1,9 @@
|
||||
<activity
|
||||
android:theme="@style/FxAccountTheme"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/fxaccount_label"
|
||||
android:icon="@drawable/fxaccount_icon"
|
||||
android:label="Status"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountSetupActivity"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountStatusActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Adding a launcher will make this activity appear on the
|
||||
Apps screen, which we only want when testing. -->
|
||||
@ -12,3 +12,48 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/FxAccountTheme"
|
||||
android:icon="@drawable/fxaccount_icon"
|
||||
android:label="Setup"
|
||||
android:clearTaskOnLaunch="true"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Adding a launcher will make this activity appear on the
|
||||
Apps screen, which we only want when testing. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/FxAccountTheme"
|
||||
android:icon="@drawable/fxaccount_icon"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountCreateAccountActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/FxAccountTheme"
|
||||
android:icon="@drawable/fxaccount_icon"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountCreateSuccessActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/FxAccountTheme"
|
||||
android:icon="@drawable/fxaccount_icon"
|
||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountSignInActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<string name="sync_link_advancedsetup"><u>&sync.link.advancedsetup.label;</u></string>
|
||||
|
||||
<!-- J-PAKE Waiting Screen -->
|
||||
|
||||
|
||||
<string name="sync_jpake_subtitle_waiting">&sync.jpake.subtitle.waiting.label;</string>
|
||||
|
||||
<!-- Account Login Screen -->
|
||||
@ -28,7 +28,7 @@
|
||||
<string name="sync_input_key">&sync.input.key.label;</string>
|
||||
<string name="sync_checkbox_server">&sync.checkbox.server.label;</string>
|
||||
<string name="sync_input_server">&sync.input.server.label;</string>
|
||||
|
||||
|
||||
<!-- Setup Fail -->
|
||||
<string name="sync_title_fail">&sync.title.fail.label;</string>
|
||||
<string name="sync_subtitle_fail">&sync.subtitle.fail.label;</string>
|
||||
@ -37,13 +37,13 @@
|
||||
<string name="sync_subtitle_nointernet">&sync.subtitle.nointernet.label;</string>
|
||||
<string name="sync_subtitle_failaccount">&sync.subtitle.failaccount.label;</string>
|
||||
<string name="sync_subtitle_failmultiple">&sync.subtitle.failmultiple.label;</string>
|
||||
|
||||
|
||||
<!-- Setup Success -->
|
||||
<string name="sync_title_success">&sync.title.success.label;</string>
|
||||
<string name="sync_subtitle_success">&sync.subtitle.success.label1;</string>
|
||||
<string name="sync_settings">&sync.settings.label;</string>
|
||||
<string name="sync_subtitle_manage">&sync.subtitle.manage.label1;</string>
|
||||
|
||||
|
||||
<!-- Pair Device -->
|
||||
<string name="sync_pair_tryagain">&sync.pair.tryagain.label;</string>
|
||||
|
||||
@ -63,10 +63,10 @@
|
||||
<string name="sync_button_cancel">&sync.button.cancel.label;</string>
|
||||
<string name="sync_button_connect">&sync.button.connect.label;</string>
|
||||
<string name="sync_button_ok">&sync.button.ok.label;</string>
|
||||
|
||||
|
||||
<!-- Account strings -->
|
||||
<string name="sync_account_label">&sync.account.label.label;</string>
|
||||
|
||||
|
||||
<!-- Bookmark folder strings -->
|
||||
<string name="bookmarks_folder_menu">&bookmarks.folder.menu.label;</string>
|
||||
<string name="bookmarks_folder_places">&bookmarks.folder.places.label;</string>
|
||||
@ -99,4 +99,54 @@
|
||||
<string name="sync_text_tab_not_sent">&sync.text.tab.not.sent.label;</string>
|
||||
|
||||
<!-- Firefox Account strings -->
|
||||
<string name="firefox_accounts">&firefox.accounts;</string>
|
||||
|
||||
<string name="fxaccount_label">&fxaccount.label;</string>
|
||||
<string name="fxaccount_policy">&fxaccount.policy;</string>
|
||||
<string name="fxaccount_forgot_password">&fxaccount.forgot.password;</string>
|
||||
<string name="fxaccount_create_account_button_label">&fxaccount.create.account.button.label;</string>
|
||||
<string name="fxaccount_sign_in_button_label">&fxaccount.sign.in.button.label;</string>
|
||||
|
||||
<string name="fxaccount_when_were_you_born">&fxaccount.when.were.you.born;</string>
|
||||
<string name="fxaccount_year_of_birth">&fxaccount.year.of.birth;</string>
|
||||
|
||||
<string name="fxaccount_email_hint">&fxaccount.email.hint;</string>
|
||||
<string name="fxaccount_password_hint">&fxaccount.password.hint;</string>
|
||||
<string name="fxaccount_password2_hint">&fxaccount.password2.hint;</string>
|
||||
|
||||
<string name="fxaccount_bad_email">&fxaccount.bad.email;</string>
|
||||
<string name="fxaccount_bad_passwords">&fxaccount.bad.passwords;</string>
|
||||
|
||||
<string name="fxaccount_sign_in_instead">&fxaccount.sign.in.instead;</string>
|
||||
<string name="fxaccount_sign_up_instead">&fxaccount.sign.up.instead;</string>
|
||||
|
||||
<string name="fxaccount_old_firefox">&fxaccount.old.firefox;</string>
|
||||
|
||||
<string name="fxaccount_options_title">&fxaccount.options.title;</string>
|
||||
<string name="fxaccount_options_configure_title">&fxaccount.options.configure.title;</string>
|
||||
|
||||
<string name="fxaccount_sign_in">&fxaccount.sign.in;</string>
|
||||
<string name="fxaccount_create_account">&fxaccount.create.account;</string>
|
||||
|
||||
<string name="fxaccount_icon_contentDescription">&fxaccount.icon.contentDescription;</string>
|
||||
<string name="fxaccount_password_hide">&fxaccount.password.hide;</string>
|
||||
<string name="fxaccount_password_show">&fxaccount.password.show;</string>
|
||||
|
||||
<string name="fxaccount_get_started">&fxaccount.get.started;</string>
|
||||
<string name="fxaccount_description">&fxaccount.description;</string>
|
||||
<string name="fxaccount_sign_up">&fxaccount.sign.up;</string>
|
||||
<string name="fxaccount_intro_contentDescription">&fxaccount.intro.contentDescription;</string>
|
||||
<string name="fxaccount_offer_resend_confirmation_email">&fxaccount.offer.resend.confirmation.email;</string>
|
||||
<string name="fxaccount_confirmation_description">&fxaccount.confirmation.description;</string>
|
||||
<string name="fxaccount_mail_contentDescription">&fxaccount.mail.contentDescription;</string>
|
||||
<string name="fxaccount_confirmation_email_sent">&fxaccount.confirmation.email.sent;</string>
|
||||
<string name="fxaccount_password_length_restriction">&fxaccount.password.length.restriction;</string>
|
||||
<string name="fxaccount_change_password">&fxaccount.change.password;</string>
|
||||
<string name="fxaccount_status_needs_verification">&fxaccount.status.needs.verification;</string>
|
||||
<string name="fxaccount_status_needs_credentials">&fxaccount.status.needs.credentials;</string>
|
||||
<string name="fxaccount_status_syncing">&fxaccount.status.syncing;</string>
|
||||
<string name="fxaccount_status_sync">&fxaccount.status.sync;</string>
|
||||
<string name="fxaccount_status_bookmarks">&fxaccount.status.bookmarks;</string>
|
||||
<string name="fxaccount_status_history">&fxaccount.status.history;</string>
|
||||
<string name="fxaccount_status_passwords">&fxaccount.status.passwords;</string>
|
||||
<string name="fxaccount_status_tabs">&fxaccount.status.tabs;</string>
|
||||
|