1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/org/gege/caldavsyncadapter/authenticator/AuthenticatorActivity.java Tue Feb 10 18:12:00 2015 +0100 1.3 @@ -0,0 +1,474 @@ 1.4 +/** 1.5 + * Copyright (c) 2012-2013, Gerald Garcia 1.6 + * 1.7 + * This file is part of Andoid Caldav Sync Adapter Free. 1.8 + * 1.9 + * Andoid Caldav Sync Adapter Free is free software: you can redistribute 1.10 + * it and/or modify it under the terms of the GNU General Public License 1.11 + * as published by the Free Software Foundation, either version 3 of the 1.12 + * License, or at your option any later version. 1.13 + * 1.14 + * Andoid Caldav Sync Adapter Free is distributed in the hope that 1.15 + * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 1.16 + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.17 + * GNU General Public License for more details. 1.18 + * 1.19 + * You should have received a copy of the GNU General Public License 1.20 + * along with Andoid Caldav Sync Adapter Free. 1.21 + * If not, see <http://www.gnu.org/licenses/>. 1.22 + * 1.23 + */ 1.24 + 1.25 +package org.gege.caldavsyncadapter.authenticator; 1.26 + 1.27 +import java.io.IOException; 1.28 +import java.io.UnsupportedEncodingException; 1.29 +import java.net.MalformedURLException; 1.30 +import java.net.URISyntaxException; 1.31 + 1.32 +import javax.xml.parsers.ParserConfigurationException; 1.33 + 1.34 +import org.apache.http.conn.HttpHostConnectException; 1.35 +import org.gege.caldavsyncadapter.R; 1.36 +import org.gege.caldavsyncadapter.caldav.CaldavFacade; 1.37 +import org.gege.caldavsyncadapter.caldav.CaldavFacade.TestConnectionResult; 1.38 +import org.xml.sax.SAXException; 1.39 + 1.40 +import android.accounts.Account; 1.41 +import android.accounts.AccountManager; 1.42 +import android.animation.Animator; 1.43 +import android.animation.AnimatorListenerAdapter; 1.44 +import android.annotation.TargetApi; 1.45 +import android.app.Activity; 1.46 +import android.content.Context; 1.47 +import android.content.pm.PackageManager.NameNotFoundException; 1.48 +import android.os.AsyncTask; 1.49 +import android.os.Build; 1.50 +import android.os.Bundle; 1.51 +import android.text.TextUtils; 1.52 +import android.util.Log; 1.53 +import android.view.KeyEvent; 1.54 +import android.view.Menu; 1.55 +import android.view.View; 1.56 +import android.view.inputmethod.EditorInfo; 1.57 +import android.widget.EditText; 1.58 +import android.widget.TextView; 1.59 +import android.widget.Toast; 1.60 + 1.61 +/** 1.62 + * Activity which displays a login screen to the user, offering registration as 1.63 + * well. 1.64 + */ 1.65 +public class AuthenticatorActivity extends Activity { 1.66 + 1.67 + private static final String TAG = "AuthenticatorActivity"; 1.68 + 1.69 + private static final String ACCOUNT_TYPE = "org.gege.caldavsyncadapter.account"; 1.70 + 1.71 + public static final String USER_DATA_URL_KEY = "USER_DATA_URL_KEY"; 1.72 + public static final String USER_DATA_USERNAME = "USER_DATA_USERNAME"; 1.73 + public static final String USER_DATA_VERSION = "USER_DATA_VERSION"; 1.74 + public static final String CURRENT_USER_DATA_VERSION = "1"; 1.75 + 1.76 + public static final String ACCOUNT_NAME_SPLITTER = "@"; 1.77 + 1.78 + /** 1.79 + * The default email to populate the email field with. 1.80 + */ 1.81 + public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL"; 1.82 + 1.83 + /** 1.84 + * Keep track of the login task to ensure we can cancel it if requested. 1.85 + */ 1.86 + private UserLoginTask mAuthTask = null; 1.87 + 1.88 + // Values for email and password at the time of the login attempt. 1.89 + private String mUser; 1.90 + private String mPassword; 1.91 + private Context mContext; 1.92 + 1.93 + // UI references. 1.94 + private EditText mUserView; 1.95 + private EditText mPasswordView; 1.96 + private View mLoginFormView; 1.97 + private View mLoginStatusView; 1.98 + private TextView mLoginStatusMessageView; 1.99 + 1.100 + private AccountManager mAccountManager; 1.101 + 1.102 + private String mURL; 1.103 + private EditText mURLView; 1.104 + 1.105 + private String mAccountname; 1.106 + private EditText mAccountnameView; 1.107 + 1.108 + public AuthenticatorActivity() { 1.109 + super(); 1.110 + 1.111 + } 1.112 + 1.113 + @Override 1.114 + protected void onCreate(Bundle savedInstanceState) { 1.115 + super.onCreate(savedInstanceState); 1.116 + 1.117 + mAccountManager = AccountManager.get(this); 1.118 + 1.119 + setContentView(R.layout.activity_authenticator); 1.120 + 1.121 + // Set up the login form. 1.122 + mUser = getIntent().getStringExtra(EXTRA_EMAIL); 1.123 + mUserView = (EditText) findViewById(R.id.user); 1.124 + mUserView.setText(mUser); 1.125 + 1.126 + mContext = getBaseContext(); 1.127 + 1.128 + mPasswordView = (EditText) findViewById(R.id.password); 1.129 + mPasswordView 1.130 + .setOnEditorActionListener(new TextView.OnEditorActionListener() { 1.131 + @Override 1.132 + public boolean onEditorAction(TextView textView, int id, 1.133 + KeyEvent keyEvent) { 1.134 + if (id == R.id.login || id == EditorInfo.IME_NULL) { 1.135 + attemptLogin(); 1.136 + return true; 1.137 + } 1.138 + return false; 1.139 + } 1.140 + }); 1.141 + 1.142 + 1.143 + mURLView = (EditText) findViewById(R.id.url); 1.144 + 1.145 + mAccountnameView = (EditText) findViewById(R.id.accountname); 1.146 + 1.147 + mLoginFormView = findViewById(R.id.login_form); 1.148 + mLoginStatusView = findViewById(R.id.login_status); 1.149 + mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message); 1.150 + 1.151 + findViewById(R.id.sign_in_button).setOnClickListener( 1.152 + new View.OnClickListener() { 1.153 + @Override 1.154 + public void onClick(View view) { 1.155 + attemptLogin(); 1.156 + } 1.157 + }); 1.158 + 1.159 + 1.160 + } 1.161 + 1.162 + @Override 1.163 + public boolean onCreateOptionsMenu(Menu menu) { 1.164 + super.onCreateOptionsMenu(menu); 1.165 + getMenuInflater().inflate(R.menu.activity_authenticator, menu); 1.166 + return true; 1.167 + } 1.168 + 1.169 + /** 1.170 + * Attempts to sign in or register the account specified by the login form. 1.171 + * If there are form errors (invalid email, missing fields, etc.), the 1.172 + * errors are presented and no actual login attempt is made. 1.173 + */ 1.174 + public void attemptLogin() { 1.175 + if (mAuthTask != null) { 1.176 + return; 1.177 + } 1.178 + 1.179 + // Reset errors. 1.180 + mUserView.setError(null); 1.181 + mPasswordView.setError(null); 1.182 + 1.183 + // Store values at the time of the login attempt. 1.184 + mUser = mUserView.getText().toString(); 1.185 + mPassword = mPasswordView.getText().toString(); 1.186 + mURL = mURLView.getText().toString(); 1.187 + mAccountname = mAccountnameView.getText().toString(); 1.188 + 1.189 + boolean cancel = false; 1.190 + View focusView = null; 1.191 + 1.192 + if (!mAccountname.equals("")) { 1.193 + Account TestAccount = new Account(mAccountname, ACCOUNT_TYPE); 1.194 + String TestUrl = mAccountManager.getUserData(TestAccount, AuthenticatorActivity.USER_DATA_URL_KEY); 1.195 + if (TestUrl != null) { 1.196 + mAccountnameView.setError(getString(R.string.error_account_already_in_use)); 1.197 + focusView = mAccountnameView; 1.198 + cancel = true; 1.199 + } 1.200 + } 1.201 + 1.202 + // Check for a valid password. 1.203 + if (TextUtils.isEmpty(mPassword)) { 1.204 + mPasswordView.setError(getString(R.string.error_field_required)); 1.205 + focusView = mPasswordView; 1.206 + cancel = true; 1.207 + } else if (mPassword.length() < 4) { 1.208 + mPasswordView.setError(getString(R.string.error_invalid_password)); 1.209 + focusView = mPasswordView; 1.210 + cancel = true; 1.211 + } 1.212 + 1.213 + // Check for a valid email address. 1.214 + if (TextUtils.isEmpty(mUser)) { 1.215 + mUserView.setError(getString(R.string.error_field_required)); 1.216 + focusView = mUserView; 1.217 + cancel = true; 1.218 + } 1.219 + //else if (!mUser.contains("@")) { 1.220 + // mUserView.setError(getString(R.string.error_invalid_email)); 1.221 + // focusView = mUserView; 1.222 + // cancel = true; 1.223 + //} 1.224 + 1.225 + if (cancel) { 1.226 + // There was an error; don't attempt login and focus the first 1.227 + // form field with an error. 1.228 + focusView.requestFocus(); 1.229 + } else { 1.230 + // Show a progress spinner, and kick off a background task to 1.231 + // perform the user login attempt. 1.232 + mLoginStatusMessageView.setText(R.string.login_progress_signing_in); 1.233 + showProgress(true); 1.234 + mAuthTask = new UserLoginTask(); 1.235 + mAuthTask.execute((Void) null); 1.236 + } 1.237 + } 1.238 + 1.239 + /** 1.240 + * Shows the progress UI and hides the login form. 1.241 + */ 1.242 + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 1.243 + private void showProgress(final boolean show) { 1.244 + // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 1.245 + // for very easy animations. If available, use these APIs to fade-in 1.246 + // the progress spinner. 1.247 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 1.248 + int shortAnimTime = getResources().getInteger( 1.249 + android.R.integer.config_shortAnimTime); 1.250 + 1.251 + mLoginStatusView.setVisibility(View.VISIBLE); 1.252 + mLoginStatusView.animate().setDuration(shortAnimTime) 1.253 + .alpha(show ? 1 : 0) 1.254 + .setListener(new AnimatorListenerAdapter() { 1.255 + @Override 1.256 + public void onAnimationEnd(Animator animation) { 1.257 + mLoginStatusView.setVisibility(show ? View.VISIBLE 1.258 + : View.GONE); 1.259 + } 1.260 + }); 1.261 + 1.262 + mLoginFormView.setVisibility(View.VISIBLE); 1.263 + mLoginFormView.animate().setDuration(shortAnimTime) 1.264 + .alpha(show ? 0 : 1) 1.265 + .setListener(new AnimatorListenerAdapter() { 1.266 + @Override 1.267 + public void onAnimationEnd(Animator animation) { 1.268 + mLoginFormView.setVisibility(show ? View.GONE 1.269 + : View.VISIBLE); 1.270 + } 1.271 + }); 1.272 + } else { 1.273 + // The ViewPropertyAnimator APIs are not available, so simply show 1.274 + // and hide the relevant UI components. 1.275 + mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 1.276 + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 1.277 + } 1.278 + } 1.279 + 1.280 + 1.281 + protected enum LoginResult { 1.282 + MalformedURLException, 1.283 + GeneralSecurityException, 1.284 + UnkonwnException, 1.285 + WrongCredentials, 1.286 + InvalidResponse, 1.287 + WrongUrl, 1.288 + ConnectionRefused, 1.289 + Success_Calendar, 1.290 + Success_Collection, 1.291 + Account_Already_In_Use 1.292 + } 1.293 + 1.294 + 1.295 + /** 1.296 + * Represents an asynchronous login/registration task used to authenticate 1.297 + * the user. 1.298 + */ 1.299 + public class UserLoginTask extends AsyncTask<Void, Void, LoginResult> { 1.300 + 1.301 + @Override 1.302 + protected LoginResult doInBackground(Void... params) { 1.303 + 1.304 + TestConnectionResult result = null; 1.305 + 1.306 + try { 1.307 + CaldavFacade facade = new CaldavFacade(mUser, mPassword, mURL); 1.308 + String version = ""; 1.309 + try { 1.310 + version = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName; 1.311 + } catch (NameNotFoundException e) { 1.312 + version = "unknown"; 1.313 + e.printStackTrace(); 1.314 + } 1.315 + facade.setVersion(version); 1.316 + result = facade.testConnection(); 1.317 + Log.i(TAG, "testConnection status="+result); 1.318 + } catch (HttpHostConnectException e) { 1.319 + Log.w(TAG,"testConnection", e); 1.320 + return LoginResult.ConnectionRefused; 1.321 + } catch (MalformedURLException e) { 1.322 + Log.w(TAG,"testConnection", e); 1.323 + return LoginResult.MalformedURLException; 1.324 + } catch (UnsupportedEncodingException e) { 1.325 + Log.w(TAG,"testConnection", e); 1.326 + return LoginResult.UnkonwnException; 1.327 + } catch (ParserConfigurationException e) { 1.328 + Log.w(TAG,"testConnection", e); 1.329 + return LoginResult.UnkonwnException; 1.330 + } catch (SAXException e) { 1.331 + Log.w(TAG,"testConnection", e); 1.332 + return LoginResult.InvalidResponse; 1.333 + } catch (IOException e) { 1.334 + Log.w(TAG,"testConnection", e); 1.335 + return LoginResult.UnkonwnException; 1.336 + } catch (URISyntaxException e) { 1.337 + Log.w(TAG,"testConnection", e); 1.338 + return LoginResult.MalformedURLException; 1.339 + } 1.340 + 1.341 + if (result == null) { 1.342 + return LoginResult.UnkonwnException; 1.343 + } 1.344 + 1.345 + switch (result) { 1.346 + 1.347 + case SUCCESS: 1.348 + boolean OldAccount = false; 1.349 + LoginResult Result = LoginResult.Success_Calendar; 1.350 + 1.351 + if (OldAccount) { 1.352 + final Account account = new Account(mUser, ACCOUNT_TYPE); 1.353 + if (mAccountManager.addAccountExplicitly(account, mPassword, null)) { 1.354 + Log.v(TAG,"new account created"); 1.355 + mAccountManager.setUserData(account, USER_DATA_URL_KEY, mURL); 1.356 + } else { 1.357 + Log.v(TAG,"no new account created"); 1.358 + Result = LoginResult.Account_Already_In_Use; 1.359 + } 1.360 + } else { 1.361 + final Account account; 1.362 + if (mAccountname.equals("")) { 1.363 + account = new Account(mUser + ACCOUNT_NAME_SPLITTER + mURL, ACCOUNT_TYPE); 1.364 + } else { 1.365 + account = new Account(mAccountname, ACCOUNT_TYPE); 1.366 + } 1.367 + if (mAccountManager.addAccountExplicitly(account, mPassword, null)) { 1.368 + Log.v(TAG,"new account created"); 1.369 + mAccountManager.setUserData(account, USER_DATA_URL_KEY, mURL); 1.370 + mAccountManager.setUserData(account, USER_DATA_USERNAME, mUser); 1.371 + mAccountManager.setUserData(account, USER_DATA_VERSION, CURRENT_USER_DATA_VERSION); 1.372 + } else { 1.373 + Log.v(TAG,"no new account created"); 1.374 + Result = LoginResult.Account_Already_In_Use; 1.375 + } 1.376 + } 1.377 + 1.378 + return Result; 1.379 + 1.380 + case WRONG_CREDENTIAL: 1.381 + return LoginResult.WrongCredentials; 1.382 + 1.383 + case WRONG_SERVER_STATUS: 1.384 + return LoginResult.InvalidResponse; 1.385 + 1.386 + case WRONG_URL: 1.387 + return LoginResult.WrongUrl; 1.388 + 1.389 + case WRONG_ANSWER: 1.390 + return LoginResult.InvalidResponse; 1.391 + 1.392 + default: 1.393 + return LoginResult.UnkonwnException; 1.394 + 1.395 + } 1.396 + 1.397 + } 1.398 + 1.399 + 1.400 + @Override 1.401 + protected void onPostExecute(final LoginResult result) { 1.402 + mAuthTask = null; 1.403 + showProgress(false); 1.404 + 1.405 + int duration = Toast.LENGTH_SHORT; 1.406 + Toast toast = null; 1.407 + 1.408 + switch (result) { 1.409 + case Success_Calendar: 1.410 + toast = Toast.makeText(getApplicationContext(), R.string.success_calendar, duration); 1.411 + toast.show(); 1.412 + finish(); 1.413 + break; 1.414 + 1.415 + case Success_Collection: 1.416 + toast = Toast.makeText(getApplicationContext(), R.string.success_collection, duration); 1.417 + toast.show(); 1.418 + finish(); 1.419 + break; 1.420 + 1.421 + case MalformedURLException: 1.422 + 1.423 + toast = Toast.makeText(getApplicationContext(), R.string.error_incorrect_url_format, duration); 1.424 + toast.show(); 1.425 + mURLView.setError(getString(R.string.error_incorrect_url_format)); 1.426 + mURLView.requestFocus(); 1.427 + break; 1.428 + case InvalidResponse: 1.429 + toast = Toast.makeText(getApplicationContext(), R.string.error_invalid_server_answer, duration); 1.430 + toast.show(); 1.431 + mURLView.setError(getString(R.string.error_invalid_server_answer)); 1.432 + mURLView.requestFocus(); 1.433 + break; 1.434 + case WrongUrl: 1.435 + toast = Toast.makeText(getApplicationContext(), R.string.error_wrong_url, duration); 1.436 + toast.show(); 1.437 + mURLView.setError(getString(R.string.error_wrong_url)); 1.438 + mURLView.requestFocus(); 1.439 + break; 1.440 + 1.441 + case WrongCredentials: 1.442 + mPasswordView.setError(getString(R.string.error_incorrect_password)); 1.443 + mPasswordView.requestFocus(); 1.444 + break; 1.445 + 1.446 + case ConnectionRefused: 1.447 + toast = Toast.makeText(getApplicationContext(), R.string.error_connection_refused, duration); 1.448 + toast.show(); 1.449 + mURLView.setError(getString(R.string.error_connection_refused)); 1.450 + mURLView.requestFocus(); 1.451 + break; 1.452 + case Account_Already_In_Use: 1.453 + toast = Toast.makeText(getApplicationContext(), R.string.error_account_already_in_use, duration); 1.454 + toast.show(); 1.455 + mURLView.setError(getString(R.string.error_account_already_in_use)); 1.456 + mURLView.requestFocus(); 1.457 + break; 1.458 + default: 1.459 + toast = Toast.makeText(getApplicationContext(), R.string.error_unkown_error, duration); 1.460 + toast.show(); 1.461 + mURLView.setError(getString(R.string.error_unkown_error)); 1.462 + mURLView.requestFocus(); 1.463 + break; 1.464 + } 1.465 + 1.466 + 1.467 + 1.468 + 1.469 + } 1.470 + 1.471 + @Override 1.472 + protected void onCancelled() { 1.473 + mAuthTask = null; 1.474 + showProgress(false); 1.475 + } 1.476 + } 1.477 +}