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

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

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

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

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

mercurial