Bug 996843 - Autocomplete email addresses in Android Firefox Accounts sign-up/sign-in. r=rnewman
authorNick Alexander <nalexander@mozilla.com>
Tue, 15 Apr 2014 15:16:15 -0700
changeset 179587 0c7af628c3dbd3b00332437ea77af98cce13633f
parent 179586 6edb3b4eba0c68077705157f1bf13ea86118417b
child 179588 8a3bc080200e450abe794d60d1349528549f93ec
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersrnewman
bugs996843
milestone31.0a1
Bug 996843 - Autocomplete email addresses in Android Firefox Accounts sign-up/sign-in. r=rnewman ======== https://github.com/mozilla-services/android-sync/commit/c41a37fad348a563d8bb9610b6a76fc3c195769d Author: Nick Alexander <nalexander@mozilla.com> Bug 996843 - Part 2: Populate email autocomplete list in onResume. ======== https://github.com/mozilla-services/android-sync/commit/418b10399115f4e19f72153eab7c1c49390d4bf7 Author: Nick Alexander <nalexander@mozilla.com> Date: Tue Apr 15 15:15:41 2014 -0700 Bug 996843 - Part 1: Make email an AutoCompleteTextView.
mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java
mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java
mobile/android/base/fxa/activities/FxAccountSignInActivity.java
mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java
mobile/android/base/resources/layout/fxaccount_email_password_view.xml
--- a/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java
@@ -1,16 +1,19 @@
 /* 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.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 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.LoginResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.background.fxa.PasswordStretcher;
@@ -19,27 +22,32 @@ import org.mozilla.gecko.fxa.FxAccountCo
 import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.ProgressDisplay;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Engaged;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.setup.Constants;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
 
+import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.text.method.PasswordTransformationMethod;
 import android.text.method.SingleLineTransformationMethod;
 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.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
 abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractActivity implements ProgressDisplay {
   public FxAccountAbstractSetupActivity() {
@@ -49,17 +57,17 @@ abstract public class FxAccountAbstractS
   protected FxAccountAbstractSetupActivity(int resume) {
     super(resume);
   }
 
   private static final String LOG_TAG = FxAccountAbstractSetupActivity.class.getSimpleName();
 
   protected int minimumPasswordLength = 8;
 
-  protected EditText emailEdit;
+  protected AutoCompleteTextView emailEdit;
   protected EditText passwordEdit;
   protected Button showPasswordButton;
   protected TextView remoteErrorTextView;
   protected Button button;
   protected ProgressBar progressBar;
 
   protected void createShowPasswordButton() {
     showPasswordButton.setOnClickListener(new OnClickListener() {
@@ -307,9 +315,59 @@ abstract public class FxAccountAbstractS
   /**
    * Factory function that produces a new PasswordStretcher instance.
    *
    * @return PasswordStretcher instance.
    */
   protected PasswordStretcher makePasswordStretcher(String password) {
     return new QuickPasswordStretcher(password);
   }
+
+  protected abstract static class GetAccountsAsyncTask extends AsyncTask<Void, Void, Account[]> {
+    protected final Context context;
+
+    public GetAccountsAsyncTask(Context context) {
+      super();
+      this.context = context;
+    }
+
+    @Override
+    protected Account[] doInBackground(Void... params) {
+      return AccountManager.get(context).getAccounts();
+    }
+  }
+
+  /**
+   * This updates UI, so needs to be done on the foreground thread.
+   */
+  protected void populateEmailAddressAutocomplete(Account[] accounts) {
+    // First a set, since we don't want repeats.
+    final Set<String> emails = new HashSet<String>();
+    for (Account account : accounts) {
+      if (!Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) {
+        continue;
+      }
+      emails.add(account.name);
+    }
+
+    // And then sorted in alphabetical order.
+    final String[] sortedEmails = emails.toArray(new String[0]);
+    Arrays.sort(sortedEmails);
+
+    final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, sortedEmails);
+    emailEdit.setAdapter(adapter);
+  }
+
+  @Override
+  public void onResume() {
+    super.onResume();
+
+    // Getting Accounts accesses databases on disk, so needs to be done on a
+    // background thread.
+    final GetAccountsAsyncTask task = new GetAccountsAsyncTask(this) {
+      @Override
+      public void onPostExecute(Account[] accounts) {
+        populateEmailAddressAutocomplete(accounts);
+      }
+    };
+    task.execute();
+  }
 }
--- a/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java
@@ -30,16 +30,17 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.text.Spannable;
 import android.text.Spanned;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 /**
@@ -61,17 +62,17 @@ public class FxAccountCreateAccountActiv
    */
   @Override
   public void onCreate(Bundle icicle) {
     Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
 
     super.onCreate(icicle);
     setContentView(R.layout.fxaccount_create_account);
 
-    emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
+    emailEdit = (AutoCompleteTextView) 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");
     remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
     button = (Button) ensureFindViewById(null, R.id.button, "create account button");
     progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
     chooseCheckBox = (CheckBox) ensureFindViewById(null, R.id.choose_what_to_sync_checkbox, "choose what to sync check box");
     selectedEngines = new HashMap<String, Boolean>();
--- a/mobile/android/base/fxa/activities/FxAccountSignInActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountSignInActivity.java
@@ -18,16 +18,17 @@ import org.mozilla.gecko.background.fxa.
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
 
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 /**
  * Activity which displays sign in screen to the user.
  */
@@ -41,17 +42,17 @@ public class FxAccountSignInActivity ext
    */
   @Override
   public void onCreate(Bundle icicle) {
     Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
 
     super.onCreate(icicle);
     setContentView(R.layout.fxaccount_sign_in);
 
-    emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
+    emailEdit = (AutoCompleteTextView) 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");
     remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
     button = (Button) ensureFindViewById(null, R.id.button, "sign in button");
     progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
 
     minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in.
     createSignInButton();
--- a/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java
@@ -23,16 +23,17 @@ import org.mozilla.gecko.fxa.authenticat
 import org.mozilla.gecko.fxa.login.Engaged;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.StateLabel;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
 
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.AutoCompleteTextView;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 /**
  * Activity which displays a screen for updating the local password.
  */
@@ -54,17 +55,17 @@ public class FxAccountUpdateCredentialsA
    */
   @Override
   public void onCreate(Bundle icicle) {
     Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
 
     super.onCreate(icicle);
     setContentView(R.layout.fxaccount_update_credentials);
 
-    emailEdit = (EditText) ensureFindViewById(null, R.id.email, "email edit");
+    emailEdit = (AutoCompleteTextView) 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");
     remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view");
     button = (Button) ensureFindViewById(null, R.id.button, "update credentials");
     progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar");
 
     minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in.
     createButton();
--- a/mobile/android/base/resources/layout/fxaccount_email_password_view.xml
+++ b/mobile/android/base/resources/layout/fxaccount_email_password_view.xml
@@ -7,26 +7,27 @@
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <LinearLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical" >
 
-        <EditText
+        <AutoCompleteTextView
             android:id="@+id/email"
             style="@style/FxAccountEditItem"
             android:layout_marginBottom="10dp"
+            android:completionThreshold="2"
             android:ems="10"
             android:hint="@string/fxaccount_email_hint"
             android:inputType="textEmailAddress" >
 
             <requestFocus />
-        </EditText>
+        </AutoCompleteTextView>
 
         <LinearLayout
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal" >
 
             <EditText
                 android:id="@+id/password"