src/org/gege/caldavsyncadapter/authenticator/AuthenticatorActivity.java

Tue, 10 Feb 2015 22:40:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 22:40:00 +0100
changeset 8
ec8af0e3fbc2
parent 0
fb9019fb1bf7
child 9
bcc778a42b8c
permissions
-rw-r--r--

Merge https://github.com/gggard/AndroidCaldavSyncAdapater/pull/206/

michael@0 1 /**
michael@0 2 * Copyright (c) 2012-2013, Gerald Garcia
michael@8 3 *
michael@0 4 * This file is part of Andoid Caldav Sync Adapter Free.
michael@0 5 *
michael@0 6 * Andoid Caldav Sync Adapter Free is free software: you can redistribute
michael@0 7 * it and/or modify it under the terms of the GNU General Public License
michael@0 8 * as published by the Free Software Foundation, either version 3 of the
michael@0 9 * License, or at your option any later version.
michael@0 10 *
michael@0 11 * Andoid Caldav Sync Adapter Free is distributed in the hope that
michael@0 12 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
michael@0 13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
michael@0 14 * GNU General Public License for more details.
michael@0 15 *
michael@0 16 * You should have received a copy of the GNU General Public License
michael@0 17 * along with Andoid Caldav Sync Adapter Free.
michael@0 18 * If not, see <http://www.gnu.org/licenses/>.
michael@8 19 *
michael@0 20 */
michael@0 21
michael@0 22 package org.gege.caldavsyncadapter.authenticator;
michael@0 23
michael@0 24 import android.accounts.Account;
michael@0 25 import android.accounts.AccountManager;
michael@0 26 import android.animation.Animator;
michael@0 27 import android.animation.AnimatorListenerAdapter;
michael@0 28 import android.annotation.TargetApi;
michael@0 29 import android.app.Activity;
michael@0 30 import android.content.Context;
michael@0 31 import android.content.pm.PackageManager.NameNotFoundException;
michael@0 32 import android.os.AsyncTask;
michael@0 33 import android.os.Build;
michael@0 34 import android.os.Bundle;
michael@8 35 import android.text.Editable;
michael@0 36 import android.text.TextUtils;
michael@8 37 import android.text.TextWatcher;
michael@0 38 import android.util.Log;
michael@0 39 import android.view.KeyEvent;
michael@0 40 import android.view.Menu;
michael@0 41 import android.view.View;
michael@0 42 import android.view.inputmethod.EditorInfo;
michael@8 43 import android.widget.CheckBox;
michael@0 44 import android.widget.EditText;
michael@0 45 import android.widget.TextView;
michael@0 46 import android.widget.Toast;
michael@0 47
michael@8 48 import org.apache.http.conn.HttpHostConnectException;
michael@8 49 import org.gege.caldavsyncadapter.Constants;
michael@8 50 import org.gege.caldavsyncadapter.R;
michael@8 51 import org.gege.caldavsyncadapter.caldav.CaldavFacade;
michael@8 52 import org.gege.caldavsyncadapter.caldav.CaldavFacade.TestConnectionResult;
michael@8 53 import org.xml.sax.SAXException;
michael@8 54
michael@8 55 import java.io.IOException;
michael@8 56 import java.io.UnsupportedEncodingException;
michael@8 57 import java.net.MalformedURLException;
michael@8 58 import java.net.URISyntaxException;
michael@8 59 import java.util.Locale;
michael@8 60
michael@8 61 import javax.xml.parsers.ParserConfigurationException;
michael@8 62
michael@0 63 /**
michael@0 64 * Activity which displays a login screen to the user, offering registration as
michael@0 65 * well.
michael@0 66 */
michael@0 67 public class AuthenticatorActivity extends Activity {
michael@0 68
michael@8 69 private static final String TAG = "AuthenticatorActivity";
michael@0 70
michael@8 71 private static final String ACCOUNT_TYPE = "org.gege.caldavsyncadapter.account";
michael@0 72
michael@8 73 public static final String USER_DATA_URL_KEY = "USER_DATA_URL_KEY";
michael@8 74 public static final String USER_DATA_USERNAME = "USER_DATA_USERNAME";
michael@8 75 public static final String USER_DATA_VERSION = "USER_DATA_VERSION";
michael@8 76 public static final String CURRENT_USER_DATA_VERSION = "1";
michael@0 77
michael@8 78 public static final String ACCOUNT_NAME_SPLITTER = "@";
michael@0 79
michael@8 80 /**
michael@8 81 * The default email to populate the email field with.
michael@8 82 */
michael@8 83 public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL";
michael@0 84
michael@8 85 /**
michael@8 86 * Keep track of the login task to ensure we can cancel it if requested.
michael@8 87 */
michael@8 88 private UserLoginTask mAuthTask = null;
michael@0 89
michael@8 90 // Values for email and password at the time of the login attempt.
michael@8 91 private String mUser;
michael@8 92 private String mPassword;
michael@8 93 private String mTrustAll;
michael@8 94 private Context mContext;
michael@0 95
michael@8 96 // UI references.
michael@8 97 private EditText mUserView;
michael@8 98 private EditText mPasswordView;
michael@8 99 private View mLoginFormView;
michael@8 100 private View mLoginStatusView;
michael@8 101 private TextView mLoginStatusMessageView;
michael@8 102 private CheckBox mTrustCheckBox;
michael@0 103
michael@8 104 private AccountManager mAccountManager;
michael@0 105
michael@8 106 private String mURL;
michael@8 107 private EditText mURLView;
michael@0 108
michael@8 109 private String mAccountname;
michael@8 110 private EditText mAccountnameView;
michael@0 111
michael@8 112 public AuthenticatorActivity() {
michael@8 113 super();
michael@0 114
michael@8 115 }
michael@0 116
michael@8 117 @Override
michael@8 118 protected void onCreate(Bundle savedInstanceState) {
michael@8 119 super.onCreate(savedInstanceState);
michael@0 120
michael@8 121 mAccountManager = AccountManager.get(this);
michael@0 122
michael@8 123 setContentView(R.layout.activity_authenticator);
michael@0 124
michael@8 125 // Set up the login form.
michael@8 126 mUser = getIntent().getStringExtra(EXTRA_EMAIL);
michael@8 127 mUserView = (EditText) findViewById(R.id.user);
michael@8 128 mUserView.setText(mUser);
michael@0 129
michael@8 130 mContext = getBaseContext();
michael@0 131
michael@8 132 mPasswordView = (EditText) findViewById(R.id.password);
michael@8 133 mPasswordView
michael@8 134 .setOnEditorActionListener(new TextView.OnEditorActionListener() {
michael@8 135 @Override
michael@8 136 public boolean onEditorAction(TextView textView, int id,
michael@8 137 KeyEvent keyEvent) {
michael@8 138 if (id == R.id.login || id == EditorInfo.IME_NULL) {
michael@8 139 attemptLogin();
michael@8 140 return true;
michael@8 141 }
michael@8 142 return false;
michael@8 143 }
michael@8 144 });
michael@0 145
michael@0 146
michael@8 147 mURLView = (EditText) findViewById(R.id.url);
michael@8 148 // if the URL start with "https" show the option to disable SSL host verification
michael@8 149 mURLView.addTextChangedListener(new TextWatcher() {
michael@8 150 @Override
michael@8 151 public void onTextChanged(CharSequence s, int start, int before, int count) {
michael@8 152 String url = ((EditText) findViewById(R.id.url)).getText().toString();
michael@8 153 int visible = url.toLowerCase(Locale.getDefault())
michael@8 154 .startsWith("https") ? View.VISIBLE : View.GONE;
michael@8 155 ((CheckBox) findViewById(R.id.trustall)).setVisibility(visible);
michael@8 156 }
michael@0 157
michael@8 158 @Override
michael@8 159 public void beforeTextChanged(CharSequence s, int start, int count,
michael@8 160 int after) {
michael@8 161 }
michael@0 162
michael@8 163 @Override
michael@8 164 public void afterTextChanged(Editable s) {
michael@8 165 }
michael@8 166 });
michael@0 167
michael@8 168 mAccountnameView = (EditText) findViewById(R.id.accountname);
michael@0 169
michael@8 170 mLoginFormView = findViewById(R.id.login_form);
michael@8 171 mLoginStatusView = findViewById(R.id.login_status);
michael@8 172 mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message);
michael@0 173
michael@8 174 findViewById(R.id.sign_in_button).setOnClickListener(
michael@8 175 new View.OnClickListener() {
michael@8 176 @Override
michael@8 177 public void onClick(View view) {
michael@8 178 attemptLogin();
michael@8 179 }
michael@8 180 }
michael@8 181 );
michael@0 182
michael@8 183 mTrustCheckBox = (CheckBox) findViewById(R.id.trustall);
michael@0 184
michael@0 185
michael@8 186 }
michael@0 187
michael@8 188 @Override
michael@8 189 public boolean onCreateOptionsMenu(Menu menu) {
michael@8 190 super.onCreateOptionsMenu(menu);
michael@8 191 return true;
michael@8 192 }
michael@0 193
michael@8 194 /**
michael@8 195 * Attempts to sign in or register the account specified by the login form.
michael@8 196 * If there are form errors (invalid email, missing fields, etc.), the
michael@8 197 * errors are presented and no actual login attempt is made.
michael@8 198 */
michael@8 199 public void attemptLogin() {
michael@8 200 if (mAuthTask != null) {
michael@8 201 return;
michael@8 202 }
michael@0 203
michael@8 204 // Reset errors.
michael@8 205 mUserView.setError(null);
michael@8 206 mPasswordView.setError(null);
michael@8 207
michael@8 208 // Store values at the time of the login attempt.
michael@8 209 mUser = mUserView.getText().toString();
michael@8 210 mPassword = mPasswordView.getText().toString();
michael@8 211 mURL = mURLView.getText().toString();
michael@8 212 mAccountname = mAccountnameView.getText().toString();
michael@8 213 mTrustAll = (mTrustCheckBox.isChecked() ? "false" : "true");
michael@8 214 boolean cancel = false;
michael@8 215 View focusView = null;
michael@8 216
michael@8 217 if (!mAccountname.equals("")) {
michael@8 218 Account TestAccount = new Account(mAccountname, ACCOUNT_TYPE);
michael@8 219 String TestUrl = mAccountManager.getUserData(TestAccount, AuthenticatorActivity.USER_DATA_URL_KEY);
michael@8 220 if (TestUrl != null) {
michael@8 221 mAccountnameView.setError(getString(R.string.error_account_already_in_use));
michael@8 222 focusView = mAccountnameView;
michael@8 223 cancel = true;
michael@8 224 }
michael@8 225 }
michael@8 226
michael@8 227 // Check for a valid password.
michael@8 228 if (TextUtils.isEmpty(mPassword)) {
michael@8 229 mPasswordView.setError(getString(R.string.error_field_required));
michael@8 230 focusView = mPasswordView;
michael@8 231 cancel = true;
michael@8 232 }
michael@8 233
michael@8 234 // Check for a valid email address.
michael@8 235 if (TextUtils.isEmpty(mUser)) {
michael@8 236 mUserView.setError(getString(R.string.error_field_required));
michael@8 237 focusView = mUserView;
michael@8 238 cancel = true;
michael@8 239 }
michael@8 240 //else if (!mUser.contains("@")) {
michael@8 241 // mUserView.setError(getString(R.string.error_invalid_email));
michael@8 242 // focusView = mUserView;
michael@8 243 // cancel = true;
michael@8 244 //}
michael@8 245
michael@8 246 if (cancel) {
michael@8 247 // There was an error; don't attempt login and focus the first
michael@8 248 // form field with an error.
michael@8 249 focusView.requestFocus();
michael@8 250 } else {
michael@8 251 // Show a progress spinner, and kick off a background task to
michael@8 252 // perform the user login attempt.
michael@8 253 mLoginStatusMessageView.setText(R.string.login_progress_signing_in);
michael@8 254 showProgress(true);
michael@8 255 mAuthTask = new UserLoginTask();
michael@8 256 mAuthTask.execute((Void) null);
michael@8 257 }
michael@8 258 }
michael@8 259
michael@8 260 /**
michael@8 261 * Shows the progress UI and hides the login form.
michael@8 262 */
michael@8 263 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
michael@8 264 private void showProgress(final boolean show) {
michael@8 265 // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
michael@8 266 // for very easy animations. If available, use these APIs to fade-in
michael@8 267 // the progress spinner.
michael@8 268 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
michael@8 269 int shortAnimTime = getResources().getInteger(
michael@8 270 android.R.integer.config_shortAnimTime);
michael@8 271
michael@8 272 mLoginStatusView.setVisibility(View.VISIBLE);
michael@8 273 mLoginStatusView.animate().setDuration(shortAnimTime)
michael@8 274 .alpha(show ? 1 : 0)
michael@8 275 .setListener(new AnimatorListenerAdapter() {
michael@8 276 @Override
michael@8 277 public void onAnimationEnd(Animator animation) {
michael@8 278 mLoginStatusView.setVisibility(show ? View.VISIBLE
michael@8 279 : View.GONE);
michael@8 280 }
michael@8 281 });
michael@8 282
michael@8 283 mLoginFormView.setVisibility(View.VISIBLE);
michael@8 284 mLoginFormView.animate().setDuration(shortAnimTime)
michael@8 285 .alpha(show ? 0 : 1)
michael@8 286 .setListener(new AnimatorListenerAdapter() {
michael@8 287 @Override
michael@8 288 public void onAnimationEnd(Animator animation) {
michael@8 289 mLoginFormView.setVisibility(show ? View.GONE
michael@8 290 : View.VISIBLE);
michael@8 291 }
michael@8 292 });
michael@8 293 } else {
michael@8 294 // The ViewPropertyAnimator APIs are not available, so simply show
michael@8 295 // and hide the relevant UI components.
michael@8 296 mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
michael@8 297 mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
michael@8 298 }
michael@8 299 }
michael@8 300
michael@8 301
michael@8 302 protected enum LoginResult {
michael@8 303 MalformedURLException,
michael@8 304 GeneralSecurityException,
michael@8 305 UnkonwnException,
michael@8 306 WrongCredentials,
michael@8 307 InvalidResponse,
michael@8 308 WrongUrl,
michael@8 309 ConnectionRefused,
michael@8 310 Success_Calendar,
michael@8 311 Success_Collection,
michael@8 312 UNTRUSTED_CERT,
michael@8 313 Account_Already_In_Use
michael@8 314 }
michael@8 315
michael@8 316
michael@8 317 /**
michael@8 318 * Represents an asynchronous login/registration task used to authenticate
michael@8 319 * the user.
michael@8 320 */
michael@8 321 public class UserLoginTask extends AsyncTask<Void, Void, LoginResult> {
michael@8 322
michael@8 323 @Override
michael@8 324 protected LoginResult doInBackground(Void... params) {
michael@8 325
michael@8 326 TestConnectionResult result = null;
michael@8 327
michael@8 328 try {
michael@8 329 CaldavFacade facade = new CaldavFacade(mUser, mPassword, mURL, mTrustAll);
michael@8 330 String version = "";
michael@8 331 try {
michael@8 332 version = mContext.getPackageManager()
michael@8 333 .getPackageInfo(mContext.getPackageName(), 0).versionName;
michael@8 334 } catch (NameNotFoundException e) {
michael@8 335 version = "unknown";
michael@8 336 e.printStackTrace();
michael@8 337 }
michael@8 338 facade.setVersion(version);
michael@8 339 result = facade.testConnection();
michael@8 340 Log.i(TAG, "testConnection status=" + result);
michael@8 341 } catch (HttpHostConnectException e) {
michael@8 342 Log.w(TAG, "testConnection", e);
michael@8 343 return LoginResult.ConnectionRefused;
michael@8 344 } catch (MalformedURLException e) {
michael@8 345 Log.w(TAG, "testConnection", e);
michael@8 346 return LoginResult.MalformedURLException;
michael@8 347 } catch (UnsupportedEncodingException e) {
michael@8 348 Log.w(TAG, "testConnection", e);
michael@8 349 return LoginResult.UnkonwnException;
michael@8 350 } catch (ParserConfigurationException e) {
michael@8 351 Log.w(TAG, "testConnection", e);
michael@8 352 return LoginResult.UnkonwnException;
michael@8 353 } catch (SAXException e) {
michael@8 354 Log.w(TAG, "testConnection", e);
michael@8 355 return LoginResult.InvalidResponse;
michael@8 356 } catch (IOException e) {
michael@8 357 Log.w(TAG, "testConnection", e);
michael@8 358 return LoginResult.UnkonwnException;
michael@8 359 } catch (URISyntaxException e) {
michael@8 360 Log.w(TAG, "testConnection", e);
michael@8 361 return LoginResult.MalformedURLException;
michael@8 362 }
michael@8 363
michael@8 364 if (result == null) {
michael@8 365 return LoginResult.UnkonwnException;
michael@8 366 }
michael@8 367
michael@8 368 switch (result) {
michael@8 369
michael@8 370 case SSL_ERROR:
michael@8 371 return LoginResult.UNTRUSTED_CERT;
michael@8 372 case SUCCESS:
michael@8 373 boolean OldAccount = false;
michael@8 374 LoginResult Result = LoginResult.Success_Calendar;
michael@8 375
michael@8 376 if (OldAccount) {
michael@8 377 final Account account = new Account(mUser, ACCOUNT_TYPE);
michael@8 378 if (mAccountManager.addAccountExplicitly(account, mPassword, null)) {
michael@8 379 Log.v(TAG, "new account created");
michael@8 380 mAccountManager.setUserData(account, USER_DATA_URL_KEY, mURL);
michael@8 381 } else {
michael@8 382 Log.v(TAG, "no new account created");
michael@8 383 Result = LoginResult.Account_Already_In_Use;
michael@8 384 }
michael@8 385 } else {
michael@8 386 final Account account;
michael@8 387 if (mAccountname.equals("")) {
michael@8 388 account = new Account(mUser + ACCOUNT_NAME_SPLITTER + mURL, ACCOUNT_TYPE);
michael@8 389 } else {
michael@8 390 account = new Account(mAccountname, ACCOUNT_TYPE);
michael@8 391 }
michael@8 392 if (mAccountManager.addAccountExplicitly(account, mPassword, null)) {
michael@8 393 Log.v(TAG, "new account created");
michael@8 394 mAccountManager.setUserData(account, USER_DATA_URL_KEY, mURL);
michael@8 395 mAccountManager.setUserData(account, USER_DATA_USERNAME, mUser);
michael@8 396 mAccountManager.setUserData(account, USER_DATA_VERSION, CURRENT_USER_DATA_VERSION);
michael@8 397 mAccountManager.setUserData(account, Constants.USER_DATA_TRUST_ALL_KEY, mTrustAll);
michael@8 398 } else {
michael@8 399 Log.v(TAG, "no new account created");
michael@8 400 Result = LoginResult.Account_Already_In_Use;
michael@8 401 }
michael@8 402 }
michael@8 403
michael@8 404 return Result;
michael@8 405
michael@8 406 case WRONG_CREDENTIAL:
michael@8 407 return LoginResult.WrongCredentials;
michael@8 408
michael@8 409 case WRONG_SERVER_STATUS:
michael@8 410 return LoginResult.InvalidResponse;
michael@8 411
michael@8 412 case WRONG_URL:
michael@8 413 return LoginResult.WrongUrl;
michael@8 414
michael@8 415 case WRONG_ANSWER:
michael@8 416 return LoginResult.InvalidResponse;
michael@8 417
michael@8 418 default:
michael@8 419 return LoginResult.UnkonwnException;
michael@8 420
michael@8 421 }
michael@8 422
michael@8 423 }
michael@8 424
michael@8 425
michael@8 426 @Override
michael@8 427 protected void onPostExecute(final LoginResult result) {
michael@8 428 mAuthTask = null;
michael@8 429 showProgress(false);
michael@8 430
michael@8 431 int duration = Toast.LENGTH_SHORT;
michael@8 432 Toast toast = null;
michael@8 433
michael@8 434 switch (result) {
michael@8 435 case Success_Calendar:
michael@8 436 toast = Toast.makeText(getApplicationContext(), R.string.success_calendar, duration);
michael@8 437 toast.show();
michael@8 438 finish();
michael@8 439 break;
michael@8 440
michael@8 441 case Success_Collection:
michael@8 442 toast = Toast.makeText(getApplicationContext(), R.string.success_collection, duration);
michael@8 443 toast.show();
michael@8 444 finish();
michael@8 445 break;
michael@8 446
michael@8 447 case MalformedURLException:
michael@8 448
michael@8 449 toast = Toast.makeText(getApplicationContext(), R.string.error_incorrect_url_format, duration);
michael@8 450 toast.show();
michael@8 451 mURLView.setError(getString(R.string.error_incorrect_url_format));
michael@8 452 mURLView.requestFocus();
michael@8 453 break;
michael@8 454 case InvalidResponse:
michael@8 455 toast = Toast.makeText(getApplicationContext(), R.string.error_invalid_server_answer, duration);
michael@8 456 toast.show();
michael@8 457 mURLView.setError(getString(R.string.error_invalid_server_answer));
michael@8 458 mURLView.requestFocus();
michael@8 459 break;
michael@8 460 case WrongUrl:
michael@8 461 toast = Toast.makeText(getApplicationContext(), R.string.error_wrong_url, duration);
michael@8 462 toast.show();
michael@8 463 mURLView.setError(getString(R.string.error_wrong_url));
michael@8 464 mURLView.requestFocus();
michael@8 465 break;
michael@8 466
michael@8 467 case GeneralSecurityException:
michael@8 468 break;
michael@8 469 case UnkonwnException:
michael@8 470 break;
michael@8 471 case WrongCredentials:
michael@8 472 mPasswordView.setError(getString(R.string.error_incorrect_password));
michael@8 473 mPasswordView.requestFocus();
michael@8 474 break;
michael@8 475
michael@8 476 case ConnectionRefused:
michael@8 477 toast = Toast.makeText(getApplicationContext(), R.string.error_connection_refused, duration);
michael@8 478 toast.show();
michael@8 479 mURLView.setError(getString(R.string.error_connection_refused));
michael@8 480 mURLView.requestFocus();
michael@8 481 break;
michael@8 482 case UNTRUSTED_CERT:
michael@8 483 toast = Toast.makeText(getApplicationContext(), getString(R.string.error_untrusted_certificate), duration);
michael@8 484 toast.show();
michael@8 485 mURLView.setError(getString(R.string.error_ssl));
michael@8 486 mURLView.requestFocus();
michael@8 487 break;
michael@8 488 case Account_Already_In_Use:
michael@8 489 toast = Toast.makeText(getApplicationContext(), R.string.error_account_already_in_use, duration);
michael@8 490 toast.show();
michael@8 491 mURLView.setError(getString(R.string.error_account_already_in_use));
michael@8 492 mURLView.requestFocus();
michael@8 493 break;
michael@8 494 default:
michael@8 495 toast = Toast.makeText(getApplicationContext(), R.string.error_unkown_error, duration);
michael@8 496 toast.show();
michael@8 497 mURLView.setError(getString(R.string.error_unkown_error));
michael@8 498 mURLView.requestFocus();
michael@8 499 break;
michael@8 500 }
michael@8 501
michael@8 502
michael@8 503 }
michael@8 504
michael@8 505 @Override
michael@8 506 protected void onCancelled() {
michael@8 507 mAuthTask = null;
michael@8 508 showProgress(false);
michael@8 509 }
michael@8 510 }
michael@8 511 }

mercurial