• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.email.activity.setup;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.text.Editable;
23 import android.text.TextWatcher;
24 import android.text.method.DigitsKeyListener;
25 import android.util.Log;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.AdapterView;
30 import android.widget.ArrayAdapter;
31 import android.widget.CheckBox;
32 import android.widget.CompoundButton;
33 import android.widget.CompoundButton.OnCheckedChangeListener;
34 import android.widget.EditText;
35 import android.widget.Spinner;
36 
37 import com.android.email.Email;
38 import com.android.email.R;
39 import com.android.email.activity.UiUtilities;
40 import com.android.email.provider.AccountBackupRestore;
41 import com.android.emailcommon.Logging;
42 import com.android.emailcommon.provider.Account;
43 import com.android.emailcommon.provider.HostAuth;
44 import com.android.emailcommon.utility.Utility;
45 
46 /**
47  * Provides UI for SMTP account settings (for IMAP/POP accounts).
48  *
49  * This fragment is used by AccountSetupOutgoing (for creating accounts) and by AccountSettingsXL
50  * (for editing existing accounts).
51  */
52 public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
53         implements OnCheckedChangeListener {
54 
55     private final static String STATE_KEY_LOADED = "AccountSetupOutgoingFragment.loaded";
56 
57     private static final int SMTP_PORT_NORMAL = 587;
58     private static final int SMTP_PORT_SSL    = 465;
59 
60     private EditText mUsernameView;
61     private EditText mPasswordView;
62     private EditText mServerView;
63     private EditText mPortView;
64     private CheckBox mRequireLoginView;
65     private Spinner mSecurityTypeView;
66 
67     // Support for lifecycle
68     private boolean mStarted;
69     private boolean mLoaded;
70 
71     /**
72      * Called to do initial creation of a fragment.  This is called after
73      * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
74      */
75     @Override
onCreate(Bundle savedInstanceState)76     public void onCreate(Bundle savedInstanceState) {
77         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
78             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onCreate");
79         }
80         super.onCreate(savedInstanceState);
81 
82         if (savedInstanceState != null) {
83             mLoaded = savedInstanceState.getBoolean(STATE_KEY_LOADED, false);
84         }
85         mBaseScheme = HostAuth.SCHEME_SMTP;
86     }
87 
88     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)89     public View onCreateView(LayoutInflater inflater, ViewGroup container,
90             Bundle savedInstanceState) {
91         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
92             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onCreateView");
93         }
94         int layoutId = mSettingsMode
95                 ? R.layout.account_settings_outgoing_fragment
96                 : R.layout.account_setup_outgoing_fragment;
97 
98         View view = inflater.inflate(layoutId, container, false);
99         Context context = getActivity();
100 
101         mUsernameView = (EditText) UiUtilities.getView(view, R.id.account_username);
102         mPasswordView = (EditText) UiUtilities.getView(view, R.id.account_password);
103         mServerView = (EditText) UiUtilities.getView(view, R.id.account_server);
104         mPortView = (EditText) UiUtilities.getView(view, R.id.account_port);
105         mRequireLoginView = (CheckBox) UiUtilities.getView(view, R.id.account_require_login);
106         mSecurityTypeView = (Spinner) UiUtilities.getView(view, R.id.account_security_type);
107         mRequireLoginView.setOnCheckedChangeListener(this);
108 
109         // Note:  Strings are shared with AccountSetupIncomingFragment
110         SpinnerOption securityTypes[] = {
111             new SpinnerOption(HostAuth.FLAG_NONE, context.getString(
112                     R.string.account_setup_incoming_security_none_label)),
113             new SpinnerOption(HostAuth.FLAG_SSL, context.getString(
114                     R.string.account_setup_incoming_security_ssl_label)),
115             new SpinnerOption(HostAuth.FLAG_SSL | HostAuth.FLAG_TRUST_ALL, context.getString(
116                     R.string.account_setup_incoming_security_ssl_trust_certificates_label)),
117             new SpinnerOption(HostAuth.FLAG_TLS, context.getString(
118                     R.string.account_setup_incoming_security_tls_label)),
119             new SpinnerOption(HostAuth.FLAG_TLS | HostAuth.FLAG_TRUST_ALL, context.getString(
120                     R.string.account_setup_incoming_security_tls_trust_certificates_label)),
121         };
122 
123         ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(context,
124                 android.R.layout.simple_spinner_item, securityTypes);
125         securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
126         mSecurityTypeView.setAdapter(securityTypesAdapter);
127 
128         // Updates the port when the user changes the security type. This allows
129         // us to show a reasonable default which the user can change.
130         mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
131             public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
132                 updatePortFromSecurityType();
133             }
134 
135             public void onNothingSelected(AdapterView<?> arg0) { }
136         });
137 
138         // Calls validateFields() which enables or disables the Next button
139         TextWatcher validationTextWatcher = new TextWatcher() {
140             public void afterTextChanged(Editable s) {
141                 validateFields();
142             }
143 
144             public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
145             public void onTextChanged(CharSequence s, int start, int before, int count) { }
146         };
147         mUsernameView.addTextChangedListener(validationTextWatcher);
148         mPasswordView.addTextChangedListener(validationTextWatcher);
149         mServerView.addTextChangedListener(validationTextWatcher);
150         mPortView.addTextChangedListener(validationTextWatcher);
151 
152         // Only allow digits in the port field.
153         mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
154 
155         // Additional setup only used while in "settings" mode
156         onCreateViewSettingsMode(view);
157 
158         return view;
159     }
160 
161     @Override
onActivityCreated(Bundle savedInstanceState)162     public void onActivityCreated(Bundle savedInstanceState) {
163         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
164             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onActivityCreated");
165         }
166         super.onActivityCreated(savedInstanceState);
167     }
168 
169     /**
170      * Called when the Fragment is visible to the user.
171      */
172     @Override
onStart()173     public void onStart() {
174         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
175             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onStart");
176         }
177         super.onStart();
178         mStarted = true;
179         loadSettings();
180     }
181 
182     /**
183      * Called when the fragment is visible to the user and actively running.
184      */
185     @Override
onResume()186     public void onResume() {
187         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
188             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onResume");
189         }
190         super.onResume();
191         validateFields();
192     }
193 
194     @Override
onPause()195     public void onPause() {
196         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
197             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onPause");
198         }
199         super.onPause();
200     }
201 
202     /**
203      * Called when the Fragment is no longer started.
204      */
205     @Override
onStop()206     public void onStop() {
207         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
208             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onStop");
209         }
210         super.onStop();
211         mStarted = false;
212     }
213 
214     /**
215      * Called when the fragment is no longer in use.
216      */
217     @Override
onDestroy()218     public void onDestroy() {
219         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
220             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onDestroy");
221         }
222         super.onDestroy();
223     }
224 
225     @Override
onSaveInstanceState(Bundle outState)226     public void onSaveInstanceState(Bundle outState) {
227         if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
228             Log.d(Logging.LOG_TAG, "AccountSetupOutgoingFragment onSaveInstanceState");
229         }
230         super.onSaveInstanceState(outState);
231 
232         outState.putBoolean(STATE_KEY_LOADED, mLoaded);
233     }
234 
235     /**
236      * Activity provides callbacks here.  This also triggers loading and setting up the UX
237      */
238     @Override
setCallback(Callback callback)239     public void setCallback(Callback callback) {
240         super.setCallback(callback);
241         if (mStarted) {
242             loadSettings();
243         }
244     }
245 
246     /**
247      * Load the current settings into the UI
248      */
loadSettings()249     private void loadSettings() {
250         if (mLoaded) return;
251 
252         HostAuth sendAuth = SetupData.getAccount().getOrCreateHostAuthSend(mContext);
253         if ((sendAuth.mFlags & HostAuth.FLAG_AUTHENTICATE) != 0) {
254             String username = sendAuth.mLogin;
255             if (username != null) {
256                 mUsernameView.setText(username);
257                 mRequireLoginView.setChecked(true);
258             }
259 
260             String password = sendAuth.mPassword;
261             if (password != null) {
262                 mPasswordView.setText(password);
263             }
264         }
265 
266         int flags = sendAuth.mFlags & ~HostAuth.FLAG_AUTHENTICATE;
267         SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, flags);
268 
269         String hostname = sendAuth.mAddress;
270         if (hostname != null) {
271             mServerView.setText(hostname);
272         }
273 
274         int port = sendAuth.mPort;
275         if (port != -1) {
276             mPortView.setText(Integer.toString(port));
277         } else {
278             updatePortFromSecurityType();
279         }
280 
281         mLoadedSendAuth = sendAuth;
282         mLoaded = true;
283         validateFields();
284     }
285 
286     /**
287      * Preflight the values in the fields and decide if it makes sense to enable the "next" button
288      */
validateFields()289     private void validateFields() {
290         if (!mLoaded) return;
291         boolean enabled =
292             Utility.isServerNameValid(mServerView) && Utility.isPortFieldValid(mPortView);
293 
294         if (enabled && mRequireLoginView.isChecked()) {
295             enabled = (Utility.isTextViewNotEmpty(mUsernameView)
296                     && Utility.isTextViewNotEmpty(mPasswordView));
297         }
298         enableNextButton(enabled);
299         // Warn (but don't prevent) if password has leading/trailing spaces
300         AccountSettingsUtils.checkPasswordSpaces(mContext, mPasswordView);
301    }
302 
303     /**
304      * implements OnCheckedChangeListener
305      */
306     @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)307     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
308         final int visibility = isChecked ? View.VISIBLE : View.GONE;
309         UiUtilities.setVisibilitySafe(getView(), R.id.account_require_login_settings, visibility);
310         UiUtilities.setVisibilitySafe(getView(), R.id.account_require_login_settings_2, visibility);
311         validateFields();
312     }
313 
getPortFromSecurityType()314     private int getPortFromSecurityType() {
315         int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
316         int port = (securityType & HostAuth.FLAG_SSL) != 0 ? SMTP_PORT_SSL : SMTP_PORT_NORMAL;
317         return port;
318     }
319 
updatePortFromSecurityType()320     private void updatePortFromSecurityType() {
321         int port = getPortFromSecurityType();
322         mPortView.setText(Integer.toString(port));
323     }
324 
325     /**
326      * Entry point from Activity after editing settings and verifying them.  Must be FLOW_MODE_EDIT.
327      * Blocking - do not call from UI Thread.
328      */
329     @Override
saveSettingsAfterEdit()330     public void saveSettingsAfterEdit() {
331         Account account = SetupData.getAccount();
332         account.mHostAuthSend.update(mContext, account.mHostAuthSend.toContentValues());
333         // Update the backup (side copy) of the accounts
334         AccountBackupRestore.backup(mContext);
335     }
336 
337     /**
338      * Entry point from Activity after entering new settings and verifying them.  For setup mode.
339      */
340     @Override
saveSettingsAfterSetup()341     public void saveSettingsAfterSetup() {
342     }
343 
344     /**
345      * Entry point from Activity, when "next" button is clicked
346      */
347     @Override
onNext()348     public void onNext() {
349         Account account = SetupData.getAccount();
350         HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
351 
352         if (mRequireLoginView.isChecked()) {
353             String userName = mUsernameView.getText().toString().trim();
354             String userPassword = mPasswordView.getText().toString();
355             sendAuth.setLogin(userName, userPassword);
356         } else {
357             sendAuth.setLogin(null, null);
358         }
359 
360         String serverAddress = mServerView.getText().toString().trim();
361         int serverPort;
362         try {
363             serverPort = Integer.parseInt(mPortView.getText().toString().trim());
364         } catch (NumberFormatException e) {
365             serverPort = getPortFromSecurityType();
366             Log.d(Logging.LOG_TAG, "Non-integer server port; using '" + serverPort + "'");
367         }
368         int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
369         sendAuth.setConnection(mBaseScheme, serverAddress, serverPort, securityType);
370         sendAuth.mDomain = null;
371 
372         mCallback.onProceedNext(SetupData.CHECK_OUTGOING, this);
373     }
374 }
375