Create AuthenticationFragment

This is one fragment that holds all types of
authentication information, e.g. password,
OAuth info, and client certificates. What gets
displayed depends upon the type of account it is
dealing with.
So far this is only used in AccountSetupIncoming,
but later it can be added to other settings fragments.
There are still some issues with this, but I'd like
to check it in sooner than later to unblock other
work.

Change-Id: Iea675ad5c1727f32ca0baa270dfa793ab7109993
diff --git a/res/drawable-hdpi/ic_add_authentication.png b/res/drawable-hdpi/ic_add_authentication.png
new file mode 100644
index 0000000..f3fde97
--- /dev/null
+++ b/res/drawable-hdpi/ic_add_authentication.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_clear_authentication.png b/res/drawable-hdpi/ic_clear_authentication.png
new file mode 100644
index 0000000..800eca4
--- /dev/null
+++ b/res/drawable-hdpi/ic_clear_authentication.png
Binary files differ
diff --git a/res/layout/account_setup_incoming_fragment.xml b/res/layout/account_setup_incoming_fragment.xml
index 4e64a5b..0754056 100644
--- a/res/layout/account_setup_incoming_fragment.xml
+++ b/res/layout/account_setup_incoming_fragment.xml
@@ -20,6 +20,7 @@
     android:layout_height="wrap_content"
     android:layout_weight="1"
     android:orientation="vertical" >
+
     <TextView
         android:text="@string/account_setup_incoming_username_label"
         android:layout_height="wrap_content"
@@ -33,19 +34,12 @@
         android:hint="@string/account_setup_incoming_username_label"
         android:inputType="textEmailAddress"
         android:imeOptions="actionNext" />
-    <TextView
-        android:text="@string/account_setup_incoming_password_label"
-        android:layout_height="wrap_content"
+
+    <FrameLayout
+        android:id="@+id/authentication_container"
         android:layout_width="match_parent"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?android:attr/textColorPrimary" />
-    <EditText
-        android:id="@+id/account_password"
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent"
-        android:hint="@string/account_setup_incoming_password_label"
-        android:inputType="textPassword"
-        android:imeOptions="actionNext" />
+        android:layout_height="wrap_content"/>
+
     <!-- This text may be changed in code if the server is IMAP, etc. -->
     <TextView
         android:id="@+id/account_server_label"
@@ -87,10 +81,6 @@
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:contentDescription="@string/account_setup_incoming_security_label" />
-    <include
-        android:id="@+id/client_certificate_selector"
-        layout="@layout/client_certificate_selector"
-        android:visibility="gone" />
     <TextView
         android:id="@+id/account_delete_policy_label"
         android:text="@string/account_setup_incoming_delete_policy_label"
@@ -103,6 +93,7 @@
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:contentDescription="@string/account_setup_incoming_delete_policy_label" />
+
     <LinearLayout
         android:id="@+id/imap_path_prefix_section"
         android:layout_width="match_parent"
@@ -122,22 +113,4 @@
             android:inputType="text"
             android:imeOptions="actionDone" />
     </LinearLayout>
-    <LinearLayout
-        android:id="@+id/device_id_section"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:visibility="gone">
-        <TextView
-            android:text="@string/account_setup_exchange_device_id_label"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:textColor="?android:attr/textColorPrimary" />
-        <TextView
-            android:id="@+id/device_id"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textColor="#ffbebebe" />
-    </LinearLayout>
 </LinearLayout>
diff --git a/res/layout/authentication_fragment.xml b/res/layout/authentication_fragment.xml
new file mode 100644
index 0000000..3ae2915
--- /dev/null
+++ b/res/layout/authentication_fragment.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/imap_authentication"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@+id/authentication_label"
+            android:text="@string/authenticiation_label"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary" />
+
+        <RelativeLayout
+            android:id="@+id/authentication_selection"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/add_authentication"
+                android:contentDescription="@string/clear_authentication_label"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:clickable="true"
+                android:src="@drawable/ic_add_authentication"
+                android:layout_centerVertical="true" />
+
+            <TextView
+                android:id="@+id/add_authentication_label"
+                android:text="@string/add_authentication_label"
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:layout_toRightOf="@id/add_authentication"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"
+                android:layout_centerVertical="true" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/imap_password_selection"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" >
+
+            <TextView
+                android:id="@+id/account_setup_basics_password_label"
+                android:text="@string/account_setup_basics_password_label"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"
+                android:layout_centerVertical="true" />
+
+            <EditText
+                android:id="@+id/imap_account_password"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_toRightOf="@id/account_setup_basics_password_label"
+                android:layout_toLeftOf="@+id/clear_password"
+                android:hint="@string/account_setup_incoming_password_label"
+                android:inputType="textPassword"
+                android:imeOptions="actionNext"
+                android:layout_centerVertical="true" />
+
+            <ImageView
+                android:id="@+id/clear_password"
+                android:contentDescription="@string/clear_authentication_label"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_alignParentRight="true"
+                android:clickable="true"
+                android:src="@drawable/ic_clear_authentication"
+                android:layout_centerVertical="true" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/oauth_selection"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <TextView
+                android:id="@+id/signed_in_with_service_label"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_toLeftOf="@+id/clear_oauth"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"
+                android:layout_centerVertical="true" />
+
+            <ImageView
+                android:id="@+id/clear_oauth"
+                android:contentDescription="@string/clear_authentication_label"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:layout_alignParentRight="true"
+                android:clickable="true"
+                android:src="@drawable/ic_clear_authentication"
+                android:layout_centerVertical="true" />
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/standard_password_selection"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <TextView
+            android:text="@string/account_setup_incoming_password_label"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorPrimary" />
+
+        <EditText
+            android:id="@+id/account_password"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:hint="@string/account_setup_incoming_password_label"
+            android:inputType="textPassword"
+            android:imeOptions="actionNext" />
+    </LinearLayout>
+
+    <include
+        android:id="@+id/client_certificate_selector"
+        layout="@layout/client_certificate_selector"
+        android:visibility="visible" />
+
+    <LinearLayout
+        android:id="@+id/device_id_section"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:visibility="gone">
+
+        <TextView
+            android:text="@string/account_setup_exchange_device_id_label"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textColor="?android:attr/textColorPrimary" />
+
+        <TextView
+            android:id="@+id/device_id"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="#ffbebebe" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/password_selector.xml b/res/layout/password_selector.xml
new file mode 100644
index 0000000..b402f10
--- /dev/null
+++ b/res/layout/password_selector.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:orientation="vertical" >
+    
+    <TextView
+        android:text="@string/account_setup_incoming_password_label"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary" />
+    <EditText
+        android:id="@+id/account_password"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:hint="@string/account_setup_incoming_password_label"
+        android:inputType="textPassword"
+        android:imeOptions="actionNext" />
+
+</LinearLayout>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 322934c..bd198df 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -30,6 +30,7 @@
         <attr name="defaultSsl" format="boolean"/>
         <attr name="offerTls" format="boolean"/>
         <attr name="offerCerts" format="boolean"/>
+        <attr name="offerOAuth" format="boolean"/>
         <attr name="offerLocalDeletes" format="boolean"/>
         <attr name="defaultLocalDeletes" format="integer"/>
         <attr name="offerPrefix" format="boolean"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c5dda07..6034ebc 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -202,6 +202,14 @@
     <string name="account_setup_basics_email_label">Email address</string>
     <!-- On "Set up email" screen, hint for account email password text field -->
     <string name="account_setup_basics_password_label">Password</string>
+    <!--  On the "Setup up email" screen, label indicating what service we are signed in with [CHAR LIMIT=40] -->
+    <string name="signed_in_with_service_label">Signed in with %s</string>
+    <!-- Label for the authentication section [CHAR LIMIT=20] -->
+    <string name="authenticiation_label">Authentication</string>
+    <!-- Label for the add authentication icon [CHAR LIMIT=40] -->
+    <string name="add_authentication_label">Add authentication</string>
+    <!-- Label for the add authentication icon [CHAR LIMIT=40] -->
+    <string name="clear_authentication_label">Clear authentication</string>
     <!-- Button name on "Set up email" screen [CHAR LIMIT=20] -->
     <string name="account_setup_basics_manual_setup_action">Manual setup</string>
     <!-- Do not translate. Button name on "Set up email" screen [CHAR LIMIT=20] TODO: This is a temporary workaround
diff --git a/res/xml/services.xml b/res/xml/services.xml
index 2d7446a..0cd01c6 100644
--- a/res/xml/services.xml
+++ b/res/xml/services.xml
@@ -33,6 +33,7 @@
      The following optional attributes default to "false":
          offerTls: whether a TLS option (e.g. STARTTLS) is offered for this service
          offerCerts: whether or not certificate authentication is an option for this service
+         offerOAuth: whether or not OAuth authentication is an option for this service
          usesSmtp: whether SMTP is used as the outgoing protocol for this service
          offerPrefix: whether a "prefix" is offered to the user (for IMAP)
          offerLocalDeletes: whether an option to delete locally is offered
@@ -83,6 +84,7 @@
         email:offerPrefix="true"
         email:syncChanges="true"
         email:inferPrefix="imap"
+        email:offerOAuth="true"
         email:offerLoadMore="true"
         email:offerMoveTo="true"
          />
diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java
index 501c8bc..eb089ec 100644
--- a/src/com/android/email/activity/setup/AccountSettings.java
+++ b/src/com/android/email/activity/setup/AccountSettings.java
@@ -388,6 +388,7 @@
         super.onBackPressed();
     }
 
+
     private void launchMailboxSettings(Intent intent) {
         final Folder folder = intent.getParcelableExtra(EditSettingsExtras.EXTRA_FOLDER);
 
@@ -402,7 +403,6 @@
         finish();
     }
 
-
     private void enableDebugMenu() {
         mShowDebugMenu = true;
         invalidateHeaders();
diff --git a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
index 817f6d0..b1d113a 100644
--- a/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
+++ b/src/com/android/email/activity/setup/AccountSetupIncomingFragment.java
@@ -17,6 +17,9 @@
 package com.android.email.activity.setup;
 
 import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -37,6 +40,7 @@
 
 import com.android.email.R;
 import com.android.email.activity.UiUtilities;
+import com.android.email.activity.setup.AuthenticationFragment.AuthenticationCallback;
 import com.android.email.provider.AccountBackupRestore;
 import com.android.email.service.EmailServiceUtils;
 import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
@@ -61,14 +65,14 @@
  * (for editing existing accounts).
  */
 public class AccountSetupIncomingFragment extends AccountServerBaseFragment
-        implements HostCallback {
+        implements HostCallback, AuthenticationCallback {
 
     private static final int CERTIFICATE_REQUEST = 0;
     private final static String STATE_KEY_CREDENTIAL = "AccountSetupIncomingFragment.credential";
     private final static String STATE_KEY_LOADED = "AccountSetupIncomingFragment.loaded";
 
     private EditText mUsernameView;
-    private EditText mPasswordView;
+    private AuthenticationFragment mAuthenticationFragment;
     private TextView mServerLabelView;
     private EditText mServerView;
     private EditText mPortView;
@@ -76,9 +80,7 @@
     private TextView mDeletePolicyLabelView;
     private Spinner mDeletePolicyView;
     private View mImapPathPrefixSectionView;
-    private View mDeviceIdSectionView;
     private EditText mImapPathPrefixView;
-    private CertificateSelector mClientCertificateSelector;
     // Delete policy as loaded from the device
     private int mLoadedDeletePolicy;
 
@@ -123,7 +125,6 @@
         final View view = inflater.inflate(layoutId, container, false);
 
         mUsernameView = UiUtilities.getView(view, R.id.account_username);
-        mPasswordView = UiUtilities.getView(view, R.id.account_password);
         mServerLabelView = UiUtilities.getView(view, R.id.account_server_label);
         mServerView = UiUtilities.getView(view, R.id.account_server);
         mPortView = UiUtilities.getView(view, R.id.account_port);
@@ -131,9 +132,7 @@
         mDeletePolicyLabelView = UiUtilities.getView(view, R.id.account_delete_policy_label);
         mDeletePolicyView = UiUtilities.getView(view, R.id.account_delete_policy);
         mImapPathPrefixSectionView = UiUtilities.getView(view, R.id.imap_path_prefix_section);
-        mDeviceIdSectionView = UiUtilities.getView(view, R.id.device_id_section);
         mImapPathPrefixView = UiUtilities.getView(view, R.id.imap_path_prefix);
-        mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
 
         // Updates the port when the user changes the security type. This allows
         // us to show a reasonable default which the user can change.
@@ -165,7 +164,6 @@
                     getString(R.string.account_setup_username_uneditable_error));
         }
         mUsernameView.addTextChangedListener(mValidationTextWatcher);
-        mPasswordView.addTextChangedListener(mValidationTextWatcher);
         mServerView.addTextChangedListener(mValidationTextWatcher);
         mPortView.addTextChangedListener(mValidationTextWatcher);
 
@@ -175,6 +173,14 @@
         // Additional setup only used while in "settings" mode
         onCreateViewSettingsMode(view);
 
+        final FragmentManager fm = getFragmentManager();
+        final FragmentTransaction ft = fm.beginTransaction();
+        mAuthenticationFragment = new AuthenticationFragment();
+        ft.add(R.id.authentication_container, mAuthenticationFragment, "AuthenticationFragment");
+        ft.commit();
+
+        mAuthenticationFragment.setAuthenticationCallback(this);
+
         return view;
     }
 
@@ -184,14 +190,13 @@
             LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onActivityCreated");
         }
         super.onActivityCreated(savedInstanceState);
-        mClientCertificateSelector.setHostActivity(this);
 
         final Context context = getActivity();
         final SetupDataFragment.SetupDataContainer container =
                 (SetupDataFragment.SetupDataContainer) context;
         mSetupData = container.getSetupData();
-
-        final HostAuth recvAuth = mSetupData.getAccount().mHostAuthRecv;
+        final Account account = mSetupData.getAccount();
+        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
         mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, recvAuth.mProtocol);
 
         if (mServiceInfo.offerLocalDeletes) {
@@ -286,14 +291,15 @@
     public void onDestroyView() {
         // Make sure we don't get callbacks after the views are supposed to be destroyed
         // and also don't hold onto them longer than we need
+        final FragmentManager fm = getFragmentManager();
+        final FragmentTransaction ft = fm.beginTransaction();
+        ft.remove(mAuthenticationFragment);
+        ft.commit();
+
         if (mUsernameView != null) {
             mUsernameView.removeTextChangedListener(mValidationTextWatcher);
         }
         mUsernameView = null;
-        if (mPasswordView != null) {
-            mPasswordView.removeTextChangedListener(mValidationTextWatcher);
-        }
-        mPasswordView = null;
         mServerLabelView = null;
         if (mServerView != null) {
             mServerView.removeTextChangedListener(mValidationTextWatcher);
@@ -310,9 +316,7 @@
         mDeletePolicyLabelView = null;
         mDeletePolicyView = null;
         mImapPathPrefixSectionView = null;
-        mDeviceIdSectionView = null;
         mImapPathPrefixView = null;
-        mClientCertificateSelector = null;
 
         super.onDestroyView();
     }
@@ -387,6 +391,8 @@
 
         final Account account = mSetupData.getAccount();
         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
+        mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, recvAuth.mProtocol);
+        mAuthenticationFragment.setAuthInfo(mServiceInfo, recvAuth);
 
         final String username = recvAuth.mLogin;
         if (username != null) {
@@ -398,14 +404,6 @@
             //}
             mUsernameView.setText(username);
         }
-        final String password = recvAuth.mPassword;
-        if (password != null) {
-            mPasswordView.setText(password);
-            // Since username is uneditable, focus on the next editable field
-            if (mSettingsMode) {
-                mPasswordView.requestFocus();
-            }
-        }
 
         if (mServiceInfo.offerPrefix) {
             final String prefix = recvAuth.mDomain;
@@ -450,14 +448,11 @@
     private void validateFields() {
         if (!mLoaded) return;
         enableNextButton(!TextUtils.isEmpty(mUsernameView.getText())
-                && !TextUtils.isEmpty(mPasswordView.getText())
+                && mAuthenticationFragment.getAuthValid()
                 && Utility.isServerNameValid(mServerView)
                 && Utility.isPortFieldValid(mPortView));
 
         mCacheLoginCredential = mUsernameView.getText().toString().trim();
-
-        // Warn (but don't prevent) if password has leading/trailing spaces
-        AccountSettingsUtils.checkPasswordSpaces(mContext, mPasswordView);
     }
 
     private int getPortFromSecurityType(boolean useSsl) {
@@ -473,20 +468,7 @@
     }
 
     public void onUseSslChanged(boolean useSsl) {
-        if (mServiceInfo.offerCerts) {
-            final int mode = useSsl ? View.VISIBLE : View.GONE;
-            mClientCertificateSelector.setVisibility(mode);
-            String deviceId = "";
-            try {
-                deviceId = Device.getDeviceId(mContext);
-            } catch (IOException e) {
-                // Not required
-            }
-            ((TextView) UiUtilities.getView(getView(), R.id.device_id)).setText(deviceId);
-
-            mDeviceIdSectionView.setVisibility(mode);
-            //UiUtilities.setVisibilitySafe(getView(), R.id.client_certificate_divider, mode);
-        }
+        mAuthenticationFragment.onUseSslChanged(useSsl);
     }
 
     private void updatePortFromSecurityType() {
@@ -545,7 +527,7 @@
 
         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
         final String userName = mUsernameView.getText().toString().trim();
-        final String userPassword = mPasswordView.getText().toString();
+        final String userPassword = mAuthenticationFragment.getPassword().toString();
         recvAuth.setLogin(userName, userPassword);
 
         final String serverAddress = mServerView.getText().toString().trim();
@@ -565,7 +547,7 @@
         } else {
             recvAuth.mDomain = null;
         }
-        recvAuth.mClientCertAlias = mClientCertificateSelector.getCertificate();
+        recvAuth.mClientCertAlias = mAuthenticationFragment.getClientCertificate();
 
         mCallback.onProceedNext(SetupDataFragment.CHECK_INCOMING, this);
         clearButtonBounce();
@@ -605,12 +587,7 @@
     }
 
     @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
-            final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
-            if (certAlias != null) {
-                mClientCertificateSelector.setCertificate(certAlias);
-            }
-        }
+    public void onValidateStateChanged() {
+        validateFields();
     }
 }
diff --git a/src/com/android/email/activity/setup/AuthenticationFragment.java b/src/com/android/email/activity/setup/AuthenticationFragment.java
new file mode 100644
index 0000000..012f9e7
--- /dev/null
+++ b/src/com/android/email/activity/setup/AuthenticationFragment.java
@@ -0,0 +1,294 @@
+package com.android.email.activity.setup;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.email.R;
+import com.android.email.activity.UiUtilities;
+import com.android.email.service.EmailServiceUtils;
+import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
+import com.android.email.view.CertificateSelector;
+import com.android.email.view.CertificateSelector.HostCallback;
+import com.android.email2.ui.MailActivityEmail;
+import com.android.emailcommon.Device;
+import com.android.emailcommon.Logging;
+import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
+import com.android.emailcommon.provider.Credential;
+import com.android.emailcommon.provider.HostAuth;
+import com.android.emailcommon.utility.CertificateRequestor;
+import com.android.mail.utils.LogUtils;
+
+import java.io.IOException;
+
+
+// FLAG:
+//  * need to handle clicking on this to clear the auth info.
+//  * need to handle getting oauth tokens
+//  * need to handle switching from password to oauth and vice versa
+
+public class AuthenticationFragment extends Fragment implements HostCallback {
+
+    private static final int CERTIFICATE_REQUEST = 0;
+
+    // Views
+    private View mImapAuthenticationView;
+    private View mImapPasswordContainer;
+    private EditText mImapPasswordView;
+    private View mImapOAuthContainer;
+    private TextView mImapOAuthView;
+    private View mImapAddAuthenticationView;
+    private View mPasswordContainer;
+    private EditText mPasswordView;
+    private CertificateSelector mClientCertificateSelector;
+    private View mDeviceIdSectionView;
+    private boolean mViewInitialized;
+
+    // Watcher
+    private TextWatcher mValidationTextWatcher;
+
+    private HostAuth mHostAuth;
+    private EmailServiceInfo mServiceInfo;
+
+    private boolean mUseOAuth;
+    private String mPassword;
+    private String mOAuthProvider;
+    private String mOAuthAccessToken;
+    private String mOAuthRefreshToken;
+
+
+    private boolean mAuthenticationValid;
+    private AuthenticationCallback mAuthenticationCallback;
+
+    public interface AuthenticationCallback {
+        public void onValidateStateChanged();
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mViewInitialized = false;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mClientCertificateSelector.setHostActivity(this);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.authentication_fragment, container, false);
+        mImapAuthenticationView = UiUtilities.getView(view, R.id.imap_authentication);
+        mImapPasswordContainer = UiUtilities.getView(view, R.id.imap_password_selection);
+        mImapPasswordView = UiUtilities.getView(view, R.id.imap_account_password);
+        mImapOAuthContainer = UiUtilities.getView(view, R.id.oauth_selection);
+        mImapOAuthView = UiUtilities.getView(view, R.id.signed_in_with_service_label);
+        mImapAddAuthenticationView = UiUtilities.getView(view, R.id.authentication_selection);
+        mPasswordContainer = UiUtilities.getView(view, R.id.standard_password_selection);
+        mPasswordView = UiUtilities.getView(view, R.id.account_password);
+        mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
+        mDeviceIdSectionView = UiUtilities.getView(view, R.id.device_id_section);
+
+        mValidationTextWatcher = new TextWatcher() {
+            @Override
+            public void afterTextChanged(Editable s) {
+                validateFields();
+            }
+
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) { }
+        };
+        mPasswordView.addTextChangedListener(mValidationTextWatcher);
+        mImapPasswordView.addTextChangedListener(mValidationTextWatcher);
+        mViewInitialized = true;
+        if (mHostAuth != null) {
+            loadInfo();
+        }
+        return view;
+    }
+
+    public void setAuthenticationCallback(final AuthenticationCallback host) {
+        mAuthenticationCallback = host;
+    }
+
+    public boolean getAuthValid() {
+        if (mServiceInfo == null || !mViewInitialized) {
+            // XXX this is kind of weird. We can get called in onStart() of our
+            // parent fragment, when we're still totally uninitialized.
+            // Once we get initialized, we need to call back to allow our parent
+            // to know what our state is.
+            return false;
+        }
+        if (mServiceInfo.offerOAuth) {
+            if (mUseOAuth) {
+                return mOAuthProvider != null;
+            } else {
+                return !TextUtils.isEmpty(mImapPasswordView.getText());
+            }
+        } else {
+            return !TextUtils.isEmpty(mPasswordView.getText());
+        }
+    }
+
+    public String getPassword() {
+        return getPasswordEditText().getText().toString();
+    }
+
+    public String getClientCertificate() {
+        return mClientCertificateSelector.getCertificate();
+    }
+
+    public String getOAuthProvider() {
+        // FLAG: need to handle this getting updated.
+        return mOAuthProvider;
+    }
+
+    private void validateFields() {
+        boolean valid = getAuthValid();
+        if (valid != mAuthenticationValid) {
+            mAuthenticationCallback.onValidateStateChanged();
+            mAuthenticationValid = valid;
+        }
+        // Warn (but don't prevent) if password has leading/trailing spaces
+        AccountSettingsUtils.checkPasswordSpaces(getActivity(), getPasswordEditText());
+    }
+
+    private EditText getPasswordEditText() {
+        if (mServiceInfo.offerOAuth) {
+            return mImapPasswordView;
+        } else {
+            return mPasswordView;
+        }
+    }
+
+    public void setAuthInfo(final EmailServiceInfo serviceInfo, final HostAuth hostAuth) {
+        mServiceInfo = serviceInfo;
+        mHostAuth = hostAuth;
+
+        if (mViewInitialized) {
+            loadInfo();
+            validateFields();
+        }
+    }
+
+    private void loadInfo() {
+        mServiceInfo = EmailServiceUtils.getServiceInfo(getActivity(), mHostAuth.mProtocol);
+
+        mClientCertificateSelector.setVisibility(
+                mServiceInfo.offerCerts ? View.VISIBLE : View.GONE);
+
+        mImapPasswordView.setText(null);
+        mPasswordView.setText(null);
+        if (mServiceInfo.offerOAuth) {
+            mPasswordContainer.setVisibility(View.GONE);
+            mImapAuthenticationView.setVisibility(View.VISIBLE);
+
+            final Credential cred = mHostAuth.getCredential(getActivity());
+            if (cred != null) {
+                // We're authenticated with OAuth.
+                mUseOAuth = true;
+                mOAuthProvider = cred.mProviderId;
+                mOAuthAccessToken = cred.mAccessToken;
+                mOAuthRefreshToken = cred.mRefreshToken;
+
+                final OAuthProvider provider = AccountSettingsUtils.findOAuthProvider(
+                        getActivity(), cred.mProviderId);
+                mImapPasswordContainer.setVisibility(View.GONE);
+                mImapOAuthContainer.setVisibility(View.VISIBLE);
+                mImapAddAuthenticationView.setVisibility(View.GONE);
+                mImapOAuthView.setText(getString(R.string.signed_in_with_service_label,
+                        provider.label));
+//            } else if (!TextUtils.isEmpty(hostAuth.mPassword)) {
+            } else {
+                // We're authenticated with a password.
+                mUseOAuth = false;
+                mPassword = mHostAuth.mPassword;
+
+                // XXX need to handle clicking on this to clear the password.
+                mImapPasswordContainer.setVisibility(View.VISIBLE);
+                mImapPasswordView.setText(mHostAuth.mPassword);
+                mImapOAuthContainer.setVisibility(View.GONE);
+                mImapAddAuthenticationView.setVisibility(View.GONE);
+/*            } else {
+ * XXX Allow us to choose what type of authentication when the password is unset.
+                // We have no authentication, we need to allow either password or oauth.
+                mUseOAuth = false;
+                mPassword = null;
+
+                // XXX need to handle clicking on this to go to the add auth fragment.
+                mImapPasswordContainer.setVisibility(View.GONE);
+                mImapOAuthContainer.setVisibility(View.GONE);
+                mImapAddAuthenticationView.setVisibility(View.VISIBLE); */
+            }
+        } else {
+            // We're using a POP or Exchange account, which does not offer oAuth.
+            mUseOAuth = false;
+            mPassword = mHostAuth.mPassword;
+
+            mImapAuthenticationView.setVisibility(View.GONE);
+            mPasswordContainer.setVisibility(View.VISIBLE);
+            mPasswordView.setVisibility(View.VISIBLE);
+            mPasswordView.setText(mHostAuth.mPassword);
+            if (TextUtils.isEmpty(mHostAuth.mPassword)) {
+                mPasswordView.requestFocus();
+            }
+        }
+        validateFields();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
+            LogUtils.d(Logging.LOG_TAG, "AuthenticationFragment onSaveInstanceState");
+        }
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    public void onCertificateRequested() {
+        final Intent intent = new Intent(CertificateRequestor.ACTION_REQUEST_CERT);
+        intent.setData(Uri.parse("eas://com.android.emailcommon/certrequest"));
+        startActivityForResult(intent, CERTIFICATE_REQUEST);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
+            final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
+            if (certAlias != null) {
+                mClientCertificateSelector.setCertificate(certAlias);
+            }
+        }
+    }
+
+
+    public void onUseSslChanged(boolean useSsl) {
+        if (mServiceInfo.offerCerts) {
+            final int mode = useSsl ? View.VISIBLE : View.GONE;
+            mClientCertificateSelector.setVisibility(mode);
+            String deviceId = "";
+            try {
+                deviceId = Device.getDeviceId(getActivity());
+            } catch (IOException e) {
+                // Not required
+            }
+            ((TextView) UiUtilities.getView(getView(), R.id.device_id)).setText(deviceId);
+            mDeviceIdSectionView.setVisibility(mode);
+        }
+    }
+}
diff --git a/src/com/android/email/service/EmailServiceUtils.java b/src/com/android/email/service/EmailServiceUtils.java
index c9b083b..488e0be 100644
--- a/src/com/android/email/service/EmailServiceUtils.java
+++ b/src/com/android/email/service/EmailServiceUtils.java
@@ -185,6 +185,7 @@
         public boolean defaultSsl;
         public boolean offerTls;
         public boolean offerCerts;
+        public boolean offerOAuth;
         public boolean usesSmtp;
         public boolean offerLocalDeletes;
         public int defaultLocalDeletes;
@@ -544,6 +545,8 @@
                         info.offerTls = ta.getBoolean(R.styleable.EmailServiceInfo_offerTls, false);
                         info.offerCerts =
                                 ta.getBoolean(R.styleable.EmailServiceInfo_offerCerts, false);
+                        info.offerOAuth =
+                                ta.getBoolean(R.styleable.EmailServiceInfo_offerOAuth, false);
                         info.offerLocalDeletes =
                             ta.getBoolean(R.styleable.EmailServiceInfo_offerLocalDeletes, false);
                         info.defaultLocalDeletes =