package com.boardgamegeek.ui; import android.accounts.Account; import android.accounts.AccountAuthenticatorActivity; import android.accounts.AccountManager; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AlertDialog.Builder; import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import com.boardgamegeek.R; import com.boardgamegeek.auth.Authenticator; import com.boardgamegeek.auth.BggCookieJar; import com.boardgamegeek.auth.NetworkAuthenticator; import com.boardgamegeek.util.ActivityUtils; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import hugo.weaving.DebugLog; import timber.log.Timber; /** * Activity which displays a login screen to the user, offering registration as well. */ public class LoginActivity extends AccountAuthenticatorActivity { private String username; private String password; @BindView(R.id.username) EditText usernameView; @BindView(R.id.password) EditText passwordView; @BindView(R.id.login_form) View loginFormView; @BindView(R.id.login_status) View loginStatusView; @BindView(R.id.login_status_message) TextView loginStatusMessageView; private UserLoginTask userLoginTask = null; private AccountManager accountManager; private boolean isRequestingNewAccount; @DebugLog @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); ButterKnife.bind(this); accountManager = AccountManager.get(this); username = getIntent().getStringExtra(ActivityUtils.KEY_USER); isRequestingNewAccount = username == null; usernameView.setText(username); usernameView.setEnabled(isRequestingNewAccount); if (!isRequestingNewAccount) { passwordView.requestFocus(); } passwordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { if (actionId == R.id.login || actionId == EditorInfo.IME_NULL) { attemptLogin(); return true; } return false; } }); } @DebugLog @Override public void onBackPressed() { if (userLoginTask != null) { userLoginTask.cancel(true); } else { super.onBackPressed(); } } @DebugLog @OnClick(R.id.sign_in_button) public void onSignInClick() { attemptLogin(); } /** * Attempts to sign in or register the account specified by the login form. If there are form errors (invalid email, * missing fields, etc.), the errors are presented and no actual login attempt is made. */ @DebugLog public void attemptLogin() { if (userLoginTask != null) { return; } // Reset errors. usernameView.setError(null); passwordView.setError(null); // Store values at the time of the login attempt. if (isRequestingNewAccount) { username = usernameView.getText().toString(); } password = passwordView.getText().toString(); View focusView = null; if (TextUtils.isEmpty(password)) { passwordView.setError(getString(R.string.error_field_required)); focusView = passwordView; } if (TextUtils.isEmpty(username)) { usernameView.setError(getString(R.string.error_field_required)); focusView = usernameView; } if (focusView != null) { // There was an error; don't attempt login and focus the first form field with an error. focusView.requestFocus(); } else { // Show a progress spinner, and kick off a background task to perform the user login attempt. loginStatusMessageView.setText(R.string.login_progress_signing_in); showProgress(true); userLoginTask = new UserLoginTask(); userLoginTask.execute((Void) null); } } /** * Shows the progress UI and hides the login form. */ @DebugLog private void showProgress(final boolean show) { int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); loginStatusView.setVisibility(View.VISIBLE); loginStatusView.animate().setDuration(shortAnimTime).alpha(show ? 1 : 0) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loginStatusView.setVisibility(show ? View.VISIBLE : View.INVISIBLE); } }); loginFormView.setVisibility(View.VISIBLE); loginFormView.animate().setDuration(shortAnimTime).alpha(show ? 0 : 1) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { loginFormView.setVisibility(show ? View.INVISIBLE : View.VISIBLE); } }); } /** * Represents an asynchronous login/registration task used to authenticate the user. */ public class UserLoginTask extends AsyncTask<Void, Void, BggCookieJar> { @Override protected BggCookieJar doInBackground(Void... params) { return NetworkAuthenticator.authenticate(username, password, "Dialog"); } @Override protected void onPostExecute(BggCookieJar bggCookieJar) { userLoginTask = null; showProgress(false); if (bggCookieJar != null) { createAccount(bggCookieJar); } else { passwordView.setError(getString(R.string.error_incorrect_password)); passwordView.requestFocus(); } } @Override protected void onCancelled() { userLoginTask = null; showProgress(false); } } @DebugLog private void createAccount(BggCookieJar bggCookieJar) { Timber.i("Creating account"); final Account account = new Account(username, Authenticator.ACCOUNT_TYPE); try { accountManager.setAuthToken(account, Authenticator.AUTH_TOKEN_TYPE, bggCookieJar.getAuthToken()); } catch (SecurityException e) { showError("Uh-oh! This isn't an error we expect to see. If you have ScorePal installed, there's a known problem that one prevents the other from signing in. We're working to resolve the issue."); return; } Bundle userData = new Bundle(); userData.putString(Authenticator.KEY_AUTH_TOKEN_EXPIRY, String.valueOf(bggCookieJar.getAuthTokenExpiry())); if (isRequestingNewAccount) { if (!accountManager.addAccountExplicitly(account, password, userData)) { Account existingAccount = Authenticator.getAccount(accountManager); if (existingAccount != null && existingAccount.name.equals(account.name)) { accountManager.setPassword(account, password); } else { passwordView.setError(getString(R.string.error_account_not_added)); return; } } } else { accountManager.setPassword(account, password); } final Intent intent = new Intent(); intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, username); intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Authenticator.ACCOUNT_TYPE); setAccountAuthenticatorResult(intent.getExtras()); setResult(RESULT_OK, intent); finish(); } @DebugLog private void showError(String message) { Builder b = new Builder(this); b.setTitle("Error").setMessage(message); b.create().show(); } }