1package com.android.email.activity.setup;
2
3import android.content.Context;
4import android.os.Bundle;
5import android.os.Parcelable;
6import android.text.Editable;
7import android.text.TextUtils;
8import android.text.TextWatcher;
9import android.util.AttributeSet;
10import android.view.LayoutInflater;
11import android.view.View;
12import android.view.View.OnClickListener;
13import android.widget.EditText;
14import android.widget.LinearLayout;
15import android.widget.TextView;
16
17import com.android.email.R;
18import com.android.email.activity.UiUtilities;
19import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
20import com.android.emailcommon.provider.Credential;
21import com.android.emailcommon.provider.HostAuth;
22import com.google.common.annotations.VisibleForTesting;
23
24public 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 {
52        public void onValidateStateChanged();
53
54        public void onRequestSignIn();
55    }
56
57    public AuthenticationView(Context context) {
58        this(context, null);
59    }
60
61    public AuthenticationView(Context context, AttributeSet attrs) {
62        this(context, attrs, 0);
63    }
64
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
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
97        public void afterTextChanged(Editable s) {
98            validateFields();
99        }
100
101        @Override
102        public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
103        @Override
104        public void onTextChanged(CharSequence s, int start, int before, int count) { }
105    }
106
107    public void setAuthenticationCallback(final AuthenticationCallback host) {
108        mAuthenticationCallback = host;
109    }
110
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
120    public void setPassword(final String password) {
121        mPasswordEdit.setText(password);
122    }
123
124    public String getPassword() {
125        return mPasswordEdit.getText().toString();
126    }
127
128    public String getOAuthProvider() {
129        return mOAuthProvider;
130    }
131
132    private void validateFields() {
133        boolean valid = getAuthValid();
134        if (valid != mAuthenticationValid) {
135            mAuthenticationCallback.onValidateStateChanged();
136            mAuthenticationValid = valid;
137        }
138    }
139
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
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
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
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
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