1 package com.android.email.activity.setup; 2 3 import android.content.Context; 4 import android.os.Bundle; 5 import android.os.Parcelable; 6 import android.text.Editable; 7 import android.text.TextUtils; 8 import android.text.TextWatcher; 9 import android.util.AttributeSet; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.EditText; 14 import android.widget.LinearLayout; 15 import android.widget.TextView; 16 17 import com.android.email.R; 18 import com.android.email.activity.UiUtilities; 19 import com.android.emailcommon.VendorPolicyLoader.OAuthProvider; 20 import com.android.emailcommon.provider.Credential; 21 import com.android.emailcommon.provider.HostAuth; 22 import com.google.common.annotations.VisibleForTesting; 23 24 public class AuthenticationView extends LinearLayout implements OnClickListener { 25 26 private final static String SUPER_STATE = "super_state"; 27 private final static String SAVE_PASSWORD = "save_password"; 28 private final static String SAVE_OFFER_OAUTH = "save_offer_oauth"; 29 private final static String SAVE_USE_OAUTH = "save_use_oauth"; 30 private final static String SAVE_OAUTH_PROVIDER = "save_oauth_provider"; 31 32 // Views 33 private TextView mAuthenticationHeader; 34 private View mPasswordWrapper; 35 private View mOAuthWrapper; 36 private View mNoAuthWrapper; 37 private TextView mPasswordLabel; 38 private EditText mPasswordEdit; 39 private TextView mOAuthLabel; 40 private View mClearPasswordView; 41 private View mClearOAuthView; 42 private View mAddAuthenticationView; 43 44 private boolean mOfferOAuth; 45 private boolean mUseOAuth; 46 private String mOAuthProvider; 47 48 private boolean mAuthenticationValid; 49 private AuthenticationCallback mAuthenticationCallback; 50 51 public interface AuthenticationCallback { onValidateStateChanged()52 public void onValidateStateChanged(); 53 onRequestSignIn()54 public void onRequestSignIn(); 55 } 56 AuthenticationView(Context context)57 public AuthenticationView(Context context) { 58 this(context, null); 59 } 60 AuthenticationView(Context context, AttributeSet attrs)61 public AuthenticationView(Context context, AttributeSet attrs) { 62 this(context, attrs, 0); 63 } 64 AuthenticationView(Context context, AttributeSet attrs, int defstyle)65 public AuthenticationView(Context context, AttributeSet attrs, int defstyle) { 66 super(context, attrs, defstyle); 67 LayoutInflater.from(context).inflate(R.layout.authentication_view, this, true); 68 } 69 70 71 @Override onFinishInflate()72 public void onFinishInflate() { 73 super.onFinishInflate(); 74 mPasswordWrapper = UiUtilities.getView(this, R.id.password_wrapper); 75 mOAuthWrapper = UiUtilities.getView(this, R.id.oauth_wrapper); 76 mPasswordEdit = UiUtilities.getView(this, R.id.password_edit); 77 mOAuthLabel = UiUtilities.getView(this, R.id.oauth_label); 78 mClearPasswordView = UiUtilities.getView(this, R.id.clear_password); 79 mClearOAuthView = UiUtilities.getView(this, R.id.clear_oauth); 80 mAddAuthenticationView = UiUtilities.getView(this, R.id.add_authentication); 81 // Don't use UiUtilities here, in some configurations, these view doesn't exist and 82 // UiUtilities throws an exception in this case. 83 mPasswordLabel = (TextView)findViewById(R.id.password_label); 84 mAuthenticationHeader = (TextView)findViewById(R.id.authentication_header); 85 86 mClearPasswordView.setOnClickListener(this); 87 mClearOAuthView.setOnClickListener(this); 88 mAddAuthenticationView.setOnClickListener(this); 89 90 final TextWatcher validationTextWatcher = new PasswordTextWatcher(); 91 mPasswordEdit.addTextChangedListener(validationTextWatcher); 92 } 93 94 private class PasswordTextWatcher implements TextWatcher { 95 96 @Override afterTextChanged(Editable s)97 public void afterTextChanged(Editable s) { 98 validateFields(); 99 } 100 101 @Override beforeTextChanged(CharSequence s, int start, int count, int after)102 public void beforeTextChanged(CharSequence s, int start, int count, int after) { } 103 @Override onTextChanged(CharSequence s, int start, int before, int count)104 public void onTextChanged(CharSequence s, int start, int before, int count) { } 105 } 106 setAuthenticationCallback(final AuthenticationCallback host)107 public void setAuthenticationCallback(final AuthenticationCallback host) { 108 mAuthenticationCallback = host; 109 } 110 getAuthValid()111 public boolean getAuthValid() { 112 if (mOfferOAuth & mUseOAuth) { 113 return mOAuthProvider != null; 114 } else { 115 return !TextUtils.isEmpty(mPasswordEdit.getText()); 116 } 117 } 118 119 @VisibleForTesting setPassword(final String password)120 public void setPassword(final String password) { 121 mPasswordEdit.setText(password); 122 } 123 getPassword()124 public String getPassword() { 125 return mPasswordEdit.getText().toString(); 126 } 127 getOAuthProvider()128 public String getOAuthProvider() { 129 return mOAuthProvider; 130 } 131 validateFields()132 private void validateFields() { 133 boolean valid = getAuthValid(); 134 if (valid != mAuthenticationValid) { 135 mAuthenticationCallback.onValidateStateChanged(); 136 mAuthenticationValid = valid; 137 } 138 } 139 setAuthInfo(final boolean offerOAuth, final HostAuth hostAuth)140 public void setAuthInfo(final boolean offerOAuth, final HostAuth hostAuth) { 141 mOfferOAuth = offerOAuth; 142 143 if (mOfferOAuth) { 144 final Credential cred = hostAuth.getCredential(getContext()); 145 if (cred != null) { 146 // We're authenticated with OAuth. 147 mUseOAuth = true; 148 mOAuthProvider = cred.mProviderId; 149 } else { 150 mUseOAuth = false; 151 } 152 } else { 153 // We're using a POP or Exchange account, which does not offer oAuth. 154 mUseOAuth = false; 155 } 156 mPasswordEdit.setText(hostAuth.mPassword); 157 158 if (mOfferOAuth && mUseOAuth) { 159 // We're authenticated with OAuth. 160 final OAuthProvider provider = AccountSettingsUtils.findOAuthProvider( 161 getContext(), mOAuthProvider); 162 mOAuthLabel.setText(getContext().getString(R.string.signed_in_with_service_label, 163 provider.label)); 164 } 165 166 updateVisibility(); 167 validateFields(); 168 } 169 updateVisibility()170 private void updateVisibility() { 171 if (mOfferOAuth) { 172 if (mAuthenticationHeader != null) { 173 mAuthenticationHeader.setVisibility(View.VISIBLE); 174 mAuthenticationHeader.setText(R.string.authentication_label); 175 } 176 if (mUseOAuth) { 177 // We're authenticated with OAuth. 178 mOAuthWrapper.setVisibility(View.VISIBLE); 179 mPasswordWrapper.setVisibility(View.GONE); 180 mAddAuthenticationView.setVisibility(View.GONE); 181 if (mPasswordLabel != null) { 182 mPasswordLabel.setVisibility(View.VISIBLE); 183 } 184 } else if (!TextUtils.isEmpty(getPassword())) { 185 // We're authenticated with a password. 186 mOAuthWrapper.setVisibility(View.GONE); 187 mPasswordWrapper.setVisibility(View.VISIBLE); 188 mAddAuthenticationView.setVisibility(View.GONE); 189 if (TextUtils.isEmpty(mPasswordEdit.getText())) { 190 mPasswordEdit.requestFocus(); 191 } 192 mClearPasswordView.setVisibility(View.VISIBLE); 193 } else { 194 // We have no authentication, we need to allow either password or oauth. 195 mOAuthWrapper.setVisibility(View.GONE); 196 mPasswordWrapper.setVisibility(View.GONE); 197 mAddAuthenticationView.setVisibility(View.VISIBLE); 198 } 199 } else { 200 // We're using a POP or Exchange account, which does not offer oAuth. 201 if (mAuthenticationHeader != null) { 202 mAuthenticationHeader.setVisibility(View.VISIBLE); 203 mAuthenticationHeader.setText(R.string.account_setup_incoming_password_label); 204 } 205 mOAuthWrapper.setVisibility(View.GONE); 206 mPasswordWrapper.setVisibility(View.VISIBLE); 207 mAddAuthenticationView.setVisibility(View.GONE); 208 mClearPasswordView.setVisibility(View.GONE); 209 if (TextUtils.isEmpty(mPasswordEdit.getText())) { 210 mPasswordEdit.requestFocus(); 211 } 212 if (mPasswordLabel != null) { 213 mPasswordLabel.setVisibility(View.GONE); 214 } 215 } 216 } 217 218 @Override onSaveInstanceState()219 public Parcelable onSaveInstanceState() { 220 Bundle bundle = new Bundle(); 221 bundle.putParcelable(SUPER_STATE, super.onSaveInstanceState()); 222 bundle.putBoolean(SAVE_OFFER_OAUTH, mOfferOAuth); 223 bundle.putBoolean(SAVE_USE_OAUTH, mUseOAuth); 224 bundle.putString(SAVE_PASSWORD, getPassword()); 225 bundle.putString(SAVE_OAUTH_PROVIDER, mOAuthProvider); 226 return bundle; 227 } 228 229 @Override onRestoreInstanceState(Parcelable parcelable)230 public void onRestoreInstanceState(Parcelable parcelable) { 231 if (parcelable instanceof Bundle) { 232 Bundle bundle = (Bundle)parcelable; 233 super.onRestoreInstanceState(bundle.getParcelable(SUPER_STATE)); 234 mOfferOAuth = bundle.getBoolean(SAVE_OFFER_OAUTH); 235 mUseOAuth = bundle.getBoolean(SAVE_USE_OAUTH); 236 mOAuthProvider = bundle.getString(SAVE_OAUTH_PROVIDER); 237 238 final String password = bundle.getString(SAVE_PASSWORD); 239 mPasswordEdit.setText(password); 240 if (!TextUtils.isEmpty(mOAuthProvider)) { 241 final OAuthProvider provider = AccountSettingsUtils.findOAuthProvider( 242 getContext(), mOAuthProvider); 243 if (provider != null) { 244 mOAuthLabel.setText(getContext().getString(R.string.signed_in_with_service_label, 245 provider.label)); 246 } 247 } 248 updateVisibility(); 249 } 250 } 251 252 @Override onClick(View view)253 public void onClick(View view) { 254 if (view == mClearPasswordView) { 255 mPasswordEdit.setText(null); 256 updateVisibility(); 257 validateFields(); 258 } else if (view == mClearOAuthView) { 259 mUseOAuth = false; 260 mOAuthProvider = null; 261 updateVisibility(); 262 validateFields(); 263 } else if (view == mAddAuthenticationView) { 264 mAuthenticationCallback.onRequestSignIn(); 265 } 266 } 267 } 268