PageRenderTime 47ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/src/com/android/email/activity/setup/AccountSetupExchangeFragment.java

https://bitbucket.org/camcory/android_packages_apps_email
Java | 490 lines | 354 code | 51 blank | 85 comment | 40 complexity | 6c357bfea5436372ff65fb9b6fd82aa6 MD5 | raw file
  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. package com.android.email.activity.setup;
  17. import android.app.Activity;
  18. import android.content.Context;
  19. import android.content.Intent;
  20. import android.net.Uri;
  21. import android.os.Bundle;
  22. import android.os.RemoteException;
  23. import android.text.Editable;
  24. import android.text.TextWatcher;
  25. import android.util.Log;
  26. import android.view.LayoutInflater;
  27. import android.view.View;
  28. import android.view.ViewGroup;
  29. import android.widget.CheckBox;
  30. import android.widget.CompoundButton;
  31. import android.widget.CompoundButton.OnCheckedChangeListener;
  32. import android.widget.EditText;
  33. import android.widget.TextView;
  34. import com.android.email.Email;
  35. import com.android.email.R;
  36. import com.android.email.activity.UiUtilities;
  37. import com.android.email.provider.AccountBackupRestore;
  38. import com.android.email.service.EmailServiceUtils;
  39. import com.android.email.view.CertificateSelector;
  40. import com.android.email.view.CertificateSelector.HostCallback;
  41. import com.android.emailcommon.Device;
  42. import com.android.emailcommon.Logging;
  43. import com.android.emailcommon.provider.Account;
  44. import com.android.emailcommon.provider.HostAuth;
  45. import com.android.emailcommon.utility.CertificateRequestor;
  46. import com.android.emailcommon.utility.Utility;
  47. import java.io.IOException;
  48. /**
  49. * Provides generic setup for Exchange accounts.
  50. *
  51. * This fragment is used by AccountSetupExchange (for creating accounts) and by AccountSettingsXL
  52. * (for editing existing accounts).
  53. */
  54. public class AccountSetupExchangeFragment extends AccountServerBaseFragment
  55. implements OnCheckedChangeListener, HostCallback {
  56. private static final int CERTIFICATE_REQUEST = 0;
  57. private final static String STATE_KEY_CREDENTIAL = "AccountSetupExchangeFragment.credential";
  58. private final static String STATE_KEY_LOADED = "AccountSetupExchangeFragment.loaded";
  59. private static final int PORT_SSL = 443;
  60. private static final int PORT_NORMAL = 80;
  61. private EditText mUsernameView;
  62. private EditText mPasswordView;
  63. private EditText mServerView;
  64. private EditText mPortView;
  65. private CheckBox mSslSecurityView;
  66. private CheckBox mTrustCertificatesView;
  67. private CertificateSelector mClientCertificateSelector;
  68. // Support for lifecycle
  69. private boolean mStarted;
  70. /* package */ boolean mLoaded;
  71. private String mCacheLoginCredential;
  72. /**
  73. * Called to do initial creation of a fragment. This is called after
  74. * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
  75. */
  76. @Override
  77. public void onCreate(Bundle savedInstanceState) {
  78. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  79. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onCreate");
  80. }
  81. super.onCreate(savedInstanceState);
  82. if (savedInstanceState != null) {
  83. mCacheLoginCredential = savedInstanceState.getString(STATE_KEY_CREDENTIAL);
  84. mLoaded = savedInstanceState.getBoolean(STATE_KEY_LOADED, false);
  85. }
  86. mBaseScheme = HostAuth.SCHEME_EAS;
  87. }
  88. @Override
  89. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  90. Bundle savedInstanceState) {
  91. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  92. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onCreateView");
  93. }
  94. int layoutId = mSettingsMode
  95. ? R.layout.account_settings_exchange_fragment
  96. : R.layout.account_setup_exchange_fragment;
  97. View view = inflater.inflate(layoutId, container, false);
  98. final Context context = getActivity();
  99. mUsernameView = UiUtilities.getView(view, R.id.account_username);
  100. mPasswordView = UiUtilities.getView(view, R.id.account_password);
  101. mServerView = UiUtilities.getView(view, R.id.account_server);
  102. mPortView = (EditText) UiUtilities.getView(view, R.id.account_port);
  103. mSslSecurityView = UiUtilities.getView(view, R.id.account_ssl);
  104. mSslSecurityView.setOnCheckedChangeListener(this);
  105. mTrustCertificatesView = UiUtilities.getView(view, R.id.account_trust_certificates);
  106. mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
  107. // Calls validateFields() which enables or disables the Next button
  108. // based on the fields' validity.
  109. TextWatcher validationTextWatcher = new TextWatcher() {
  110. @Override
  111. public void afterTextChanged(Editable s) {
  112. validateFields();
  113. }
  114. @Override
  115. public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
  116. @Override
  117. public void onTextChanged(CharSequence s, int start, int before, int count) { }
  118. };
  119. // We're editing an existing account; don't allow modification of the user name
  120. if (mSettingsMode) {
  121. makeTextViewUneditable(mUsernameView,
  122. getString(R.string.account_setup_username_uneditable_error));
  123. }
  124. mUsernameView.addTextChangedListener(validationTextWatcher);
  125. mPasswordView.addTextChangedListener(validationTextWatcher);
  126. mServerView.addTextChangedListener(validationTextWatcher);
  127. mPortView.addTextChangedListener(validationTextWatcher);
  128. EditText lastView = mServerView;
  129. lastView.setOnEditorActionListener(mDismissImeOnDoneListener);
  130. String deviceId = "";
  131. try {
  132. deviceId = Device.getDeviceId(context);
  133. } catch (IOException e) {
  134. // Not required
  135. }
  136. ((TextView) UiUtilities.getView(view, R.id.device_id)).setText(deviceId);
  137. // Additional setup only used while in "settings" mode
  138. onCreateViewSettingsMode(view);
  139. return view;
  140. }
  141. @Override
  142. public void onActivityCreated(Bundle savedInstanceState) {
  143. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  144. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onActivityCreated");
  145. }
  146. super.onActivityCreated(savedInstanceState);
  147. mClientCertificateSelector.setHostActivity(this);
  148. }
  149. /**
  150. * Called when the Fragment is visible to the user.
  151. */
  152. @Override
  153. public void onStart() {
  154. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  155. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onStart");
  156. }
  157. super.onStart();
  158. mStarted = true;
  159. loadSettings(SetupData.getAccount());
  160. }
  161. /**
  162. * Called when the fragment is visible to the user and actively running.
  163. */
  164. @Override
  165. public void onResume() {
  166. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  167. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onResume");
  168. }
  169. super.onResume();
  170. validateFields();
  171. }
  172. @Override
  173. public void onPause() {
  174. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  175. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onPause");
  176. }
  177. super.onPause();
  178. }
  179. /**
  180. * Called when the Fragment is no longer started.
  181. */
  182. @Override
  183. public void onStop() {
  184. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  185. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onStop");
  186. }
  187. super.onStop();
  188. mStarted = false;
  189. }
  190. /**
  191. * Called when the fragment is no longer in use.
  192. */
  193. @Override
  194. public void onDestroy() {
  195. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  196. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onDestroy");
  197. }
  198. super.onDestroy();
  199. }
  200. @Override
  201. public void onSaveInstanceState(Bundle outState) {
  202. if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
  203. Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onSaveInstanceState");
  204. }
  205. super.onSaveInstanceState(outState);
  206. outState.putString(STATE_KEY_CREDENTIAL, mCacheLoginCredential);
  207. outState.putBoolean(STATE_KEY_LOADED, mLoaded);
  208. }
  209. /**
  210. * Activity provides callbacks here. This also triggers loading and setting up the UX
  211. */
  212. @Override
  213. public void setCallback(Callback callback) {
  214. super.setCallback(callback);
  215. if (mStarted) {
  216. loadSettings(SetupData.getAccount());
  217. }
  218. }
  219. /**
  220. * Force the given account settings to be loaded using {@link #loadSettings(Account)}.
  221. *
  222. * @return true if the loaded values pass validation
  223. */
  224. private boolean forceLoadSettings(Account account) {
  225. mLoaded = false;
  226. return loadSettings(account);
  227. }
  228. private int getPortFromSecurityType() {
  229. boolean useSsl = mSslSecurityView.isChecked();
  230. int port = useSsl ? PORT_SSL : PORT_NORMAL;
  231. return port;
  232. }
  233. private void updatePortFromSecurityType() {
  234. int port = getPortFromSecurityType();
  235. mPortView.setText(Integer.toString(port));
  236. }
  237. /**
  238. * Load the given account settings into the UI and then ensure the settings are valid.
  239. * As an optimization, if the settings have already been loaded, the UI will not be
  240. * updated, but, the account fields will still be validated.
  241. *
  242. * @return true if the loaded values pass validation
  243. */
  244. /*package*/ boolean loadSettings(Account account) {
  245. if (mLoaded) return validateFields();
  246. HostAuth hostAuth = account.mHostAuthRecv;
  247. String userName = hostAuth.mLogin;
  248. if (userName != null) {
  249. // Add a backslash to the start of the username, but only if the username has no
  250. // backslash in it.
  251. if (userName.indexOf('\\') < 0) {
  252. userName = "\\" + userName;
  253. }
  254. mUsernameView.setText(userName);
  255. }
  256. if (hostAuth.mPassword != null) {
  257. mPasswordView.setText(hostAuth.mPassword);
  258. // Since username is uneditable, focus on the next editable field
  259. if (mSettingsMode) {
  260. mPasswordView.requestFocus();
  261. }
  262. }
  263. String protocol = hostAuth.mProtocol;
  264. if (protocol == null || !protocol.startsWith("eas")) {
  265. throw new Error("Unknown account type: " + protocol);
  266. }
  267. if (hostAuth.mAddress != null) {
  268. mServerView.setText(hostAuth.mAddress);
  269. }
  270. boolean ssl = 0 != (hostAuth.mFlags & HostAuth.FLAG_SSL);
  271. boolean trustCertificates = 0 != (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL);
  272. mSslSecurityView.setChecked(ssl);
  273. mTrustCertificatesView.setChecked(trustCertificates);
  274. if (hostAuth.mClientCertAlias != null) {
  275. mClientCertificateSelector.setCertificate(hostAuth.mClientCertAlias);
  276. }
  277. onUseSslChanged(ssl);
  278. int port = hostAuth.mPort;
  279. if (port != HostAuth.PORT_UNKNOWN) {
  280. mPortView.setText(Integer.toString(port));
  281. } else {
  282. updatePortFromSecurityType();
  283. }
  284. mLoadedRecvAuth = hostAuth;
  285. mLoaded = true;
  286. return validateFields();
  287. }
  288. private boolean usernameFieldValid(EditText usernameView) {
  289. return Utility.isTextViewNotEmpty(usernameView) &&
  290. !usernameView.getText().toString().equals("\\");
  291. }
  292. /**
  293. * Check the values in the fields and decide if it makes sense to enable the "next" button
  294. * @return true if all fields are valid, false if any fields are incomplete
  295. */
  296. private boolean validateFields() {
  297. if (!mLoaded) return false;
  298. boolean enabled = usernameFieldValid(mUsernameView)
  299. && Utility.isTextViewNotEmpty(mPasswordView)
  300. && Utility.isServerNameValid(mServerView);
  301. enableNextButton(enabled);
  302. // Warn (but don't prevent) if password has leading/trailing spaces
  303. AccountSettingsUtils.checkPasswordSpaces(mContext, mPasswordView);
  304. return enabled;
  305. }
  306. @Override
  307. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
  308. if (buttonView.getId() == R.id.account_ssl) {
  309. onUseSslChanged(isChecked);
  310. }
  311. }
  312. public void onUseSslChanged(boolean useSsl) {
  313. int mode = useSsl ? View.VISIBLE : View.GONE;
  314. mTrustCertificatesView.setVisibility(mode);
  315. UiUtilities.setVisibilitySafe(getView(), R.id.account_trust_certificates_divider, mode);
  316. mClientCertificateSelector.setVisibility(mode);
  317. UiUtilities.setVisibilitySafe(getView(), R.id.client_certificate_divider, mode);
  318. }
  319. @Override
  320. public void onCheckSettingsComplete(final int result) {
  321. if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_CLIENT_CERTIFICATE_NEEDED) {
  322. mSslSecurityView.setChecked(true);
  323. onCertificateRequested();
  324. return;
  325. }
  326. super.onCheckSettingsComplete(result);
  327. }
  328. /**
  329. * Entry point from Activity after editing settings and verifying them. Must be FLOW_MODE_EDIT.
  330. * Blocking - do not call from UI Thread.
  331. */
  332. @Override
  333. public void saveSettingsAfterEdit() {
  334. Account account = SetupData.getAccount();
  335. account.mHostAuthRecv.update(mContext, account.mHostAuthRecv.toContentValues());
  336. account.mHostAuthSend.update(mContext, account.mHostAuthSend.toContentValues());
  337. // For EAS, notify ExchangeService that the password has changed
  338. try {
  339. EmailServiceUtils.getExchangeService(mContext, null).hostChanged(account.mId);
  340. } catch (RemoteException e) {
  341. // Nothing to be done if this fails
  342. }
  343. // Update the backup (side copy) of the accounts
  344. AccountBackupRestore.backup(mContext);
  345. }
  346. /**
  347. * Entry point from Activity after entering new settings and verifying them. For setup mode.
  348. */
  349. @Override
  350. public void saveSettingsAfterSetup() {
  351. }
  352. /**
  353. * Entry point from Activity after entering new settings and verifying them. For setup mode.
  354. */
  355. public boolean setHostAuthFromAutodiscover(HostAuth newHostAuth) {
  356. Account account = SetupData.getAccount();
  357. account.mHostAuthSend = newHostAuth;
  358. account.mHostAuthRecv = newHostAuth;
  359. // Auto discovery may have changed the auth settings; force load them
  360. return forceLoadSettings(account);
  361. }
  362. /**
  363. * Implements AccountCheckSettingsFragment.Callbacks
  364. */
  365. @Override
  366. public void onAutoDiscoverComplete(int result, HostAuth hostAuth) {
  367. AccountSetupExchange activity = (AccountSetupExchange) getActivity();
  368. activity.onAutoDiscoverComplete(result, hostAuth);
  369. }
  370. /**
  371. * Entry point from Activity, when "next" button is clicked
  372. */
  373. @Override
  374. public void onNext() {
  375. Account account = SetupData.getAccount();
  376. String userName = mUsernameView.getText().toString().trim();
  377. if (userName.startsWith("\\")) {
  378. userName = userName.substring(1);
  379. }
  380. mCacheLoginCredential = userName;
  381. String userPassword = mPasswordView.getText().toString();
  382. int flags = 0;
  383. if (mSslSecurityView.isChecked()) {
  384. flags |= HostAuth.FLAG_SSL;
  385. }
  386. if (mTrustCertificatesView.isChecked()) {
  387. flags |= HostAuth.FLAG_TRUST_ALL;
  388. }
  389. String certAlias = mClientCertificateSelector.getCertificate();
  390. String serverAddress = mServerView.getText().toString().trim();
  391. String portText = mPortView.getText().toString().trim();
  392. int port;
  393. try {
  394. port = Integer.parseInt(portText);
  395. } catch (NumberFormatException e) {
  396. // Worst case, do something sensible
  397. port = mSslSecurityView.isChecked() ? 443 : 80;
  398. Log.d(Logging.LOG_TAG, "Non-integer server port; using '" + port + "'");
  399. }
  400. HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
  401. sendAuth.setLogin(userName, userPassword);
  402. sendAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
  403. sendAuth.mDomain = null;
  404. HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
  405. recvAuth.setLogin(userName, userPassword);
  406. recvAuth.setConnection(mBaseScheme, serverAddress, port, flags, certAlias);
  407. recvAuth.mDomain = null;
  408. // Check for a duplicate account (requires async DB work) and if OK, proceed with check
  409. startDuplicateTaskCheck(account.mId, serverAddress, mCacheLoginCredential,
  410. SetupData.CHECK_INCOMING);
  411. }
  412. @Override
  413. public void onCertificateRequested() {
  414. Intent intent = new Intent(CertificateRequestor.ACTION_REQUEST_CERT);
  415. intent.setData(Uri.parse("eas://com.android.emailcommon/certrequest"));
  416. startActivityForResult(intent, CERTIFICATE_REQUEST);
  417. }
  418. @Override
  419. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  420. if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
  421. String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
  422. if (certAlias != null) {
  423. mClientCertificateSelector.setCertificate(certAlias);
  424. }
  425. }
  426. }
  427. }