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