Refactoring in LoginActivity

This commit is contained in:
addshore 2017-05-14 12:10:44 +02:00
parent 1a288176bb
commit a1d8008901
3 changed files with 236 additions and 185 deletions

View file

@ -1,15 +1,9 @@
package fr.free.nrw.commons.auth;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.text.Editable;
@ -21,18 +15,11 @@ import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.EventLog;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import timber.log.Timber;
@ -40,155 +27,49 @@ public class LoginActivity extends AccountAuthenticatorActivity {
public static final String PARAM_USERNAME = "fr.free.nrw.commons.login.username";
private CommonsApplication app;
private SharedPreferences prefs = null;
Button loginButton;
Button signupButton;
EditText usernameEdit;
private Button loginButton;
private EditText usernameEdit;
EditText passwordEdit;
EditText twoFactorEdit;
ProgressDialog dialog;
private class LoginTask extends AsyncTask<String, String, String> {
Activity context;
String username;
String password;
String twoFactorCode = "";
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Timber.d("Login done!");
EventLog.schema(CommonsApplication.EVENT_LOGIN_ATTEMPT)
.param("username", username)
.param("result", result)
.log();
if (result.equals("PASS")) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Toast successToast = Toast.makeText(context, R.string.login_success, Toast.LENGTH_SHORT);
successToast.show();
Account account = new Account(username, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
boolean accountCreated = AccountManager.get(context).addAccountExplicitly(account, password, null);
Bundle extras = context.getIntent().getExtras();
if (extras != null) {
Timber.d("Bundle of extras: %s", extras);
if (accountCreated) { // Pass the new account back to the account manager
AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
Bundle authResult = new Bundle();
authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username);
authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
if (response != null) {
response.onResult(authResult);
}
}
}
// FIXME: If the user turns it off, it shouldn't be auto turned back on
ContentResolver.setSyncAutomatically(account, ContributionsContentProvider.AUTHORITY, true); // Enable sync by default!
ContentResolver.setSyncAutomatically(account, ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
Intent intent = new Intent(context, ContributionsActivity.class);
startActivity(intent);
finish();
} else {
int response;
// Match known failure message codes and provide messages
if(result.equals("NetworkFailure")) {
// Matches NetworkFailure which is created by the doInBackground method
response = R.string.login_failed_network;
} else if (result.toLowerCase().contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
// Matches nosuchuser, nosuchusershort, noname
response = R.string.login_failed_username;
emptySensitiveEditFields();
} else if (result.toLowerCase().contains("wrongpassword".toLowerCase())) {
// Matches wrongpassword, wrongpasswordempty
response = R.string.login_failed_password;
emptySensitiveEditFields();
} else if (result.toLowerCase().contains("throttle".toLowerCase())) {
// Matches unknown throttle error codes
response = R.string.login_failed_throttled;
} else if (result.toLowerCase().contains("userblocked".toLowerCase())) {
// Matches login-userblocked
response = R.string.login_failed_blocked;
} else if (result.equals("2FA")){
twoFactorEdit.setVisibility(View.VISIBLE);
response = R.string.login_failed_2fa_needed;
} else {
// Occurs with unhandled login failure codes
Timber.d("Login failed with reason: %s", result);
response = R.string.login_failed_generic;
}
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
dialog.cancel();
}
}
private void emptySensitiveEditFields() {
passwordEdit.setText("");
twoFactorEdit.setText("");
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setIndeterminate(true);
dialog.setTitle(getString(R.string.logging_in_title));
dialog.setMessage(getString(R.string.logging_in_message));
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
LoginTask(Activity context) {
this.context = context;
}
@Override
protected String doInBackground(String... params) {
username = params[0];
password = params[1];
if(params.length > 2) {
twoFactorCode = params[2];
}
try {
if(twoFactorCode.isEmpty()) {
return app.getApi().login(username, password);
} else {
return app.getApi().login(username, password, twoFactorCode);
}
} catch (IOException e) {
// Do something better!
return "NetworkFailure";
}
}
}
ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = (CommonsApplication) this.getApplicationContext();
setContentView(R.layout.activity_login);
final LoginActivity that = this;
loginButton = (Button) findViewById(R.id.loginButton);
signupButton = (Button) findViewById(R.id.signupButton);
Button signupButton = (Button) findViewById(R.id.signupButton);
usernameEdit = (EditText) findViewById(R.id.loginUsername);
passwordEdit = (EditText) findViewById(R.id.loginPassword);
twoFactorEdit = (EditText) findViewById(R.id.loginTwoFactor);
final LoginActivity that = this;
prefs = getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
TextWatcher loginEnabler = new TextWatcher() {
TextWatcher loginEnabler = newLoginTextWatcher();
usernameEdit.addTextChangedListener(loginEnabler);
passwordEdit.addTextChangedListener(loginEnabler);
twoFactorEdit.addTextChangedListener(loginEnabler);
passwordEdit.setOnEditorActionListener( newLoginInputActionListener() );
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
that.performLogin();
}
});
signupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { that.signUp(v); }
});
}
private TextWatcher newLoginTextWatcher() {
return new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) { }
@ -208,11 +89,10 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
}
};
}
usernameEdit.addTextChangedListener(loginEnabler);
passwordEdit.addTextChangedListener(loginEnabler);
twoFactorEdit.addTextChangedListener(loginEnabler);
passwordEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
private TextView.OnEditorActionListener newLoginInputActionListener() {
return new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (loginButton.isEnabled()) {
@ -226,36 +106,28 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
return false;
}
});
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
that.performLogin();
}
});
};
}
@Override
protected void onResume() {
super.onResume();
if (prefs.getBoolean("firstrun", true)) {
// Do first run stuff here then set 'firstrun' as false
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivity(welcomeIntent);
this.startWelcomeIntent();
prefs.edit().putBoolean("firstrun", false).apply();
}
}
private void startWelcomeIntent() {
Intent welcomeIntent = new Intent(this, WelcomeActivity.class);
startActivity(welcomeIntent);
}
@Override
protected void onDestroy() {
try {
// To prevent leaked window when finish() is called, see http://stackoverflow.com/questions/32065854/activity-has-leaked-window-at-alertdialog-show-method
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
@ -264,21 +136,27 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
private void performLogin() {
String username = usernameEdit.getText().toString();
// Because Mediawiki is upercase-first-char-then-case-sensitive :)
String canonicalUsername = Utils.capitalize(username.substring(0,1)) + username.substring(1);
String password = passwordEdit.getText().toString();
String twoFactorCode = twoFactorEdit.getText().toString();
Timber.d("Login to start!");
LoginTask task = new LoginTask(this);
if(twoFactorCode.isEmpty()) {
task.execute(canonicalUsername, password);
} else {
task.execute(canonicalUsername, password, twoFactorCode);
LoginTask task = getLoginTask();
task.execute();
}
private LoginTask getLoginTask() {
return new LoginTask(
this,
canonicializeUsername( usernameEdit.getText().toString() ),
passwordEdit.getText().toString(),
twoFactorEdit.getText().toString()
);
}
/**
* Because Mediawiki is upercase-first-char-then-case-sensitive :)
* @param username String
* @return String canonicial username
*/
private String canonicializeUsername( String username ) {
return Utils.capitalize(username.substring(0,1)) + username.substring(1);
}
@Override
@ -291,9 +169,38 @@ public class LoginActivity extends AccountAuthenticatorActivity {
return super.onOptionsItemSelected(item);
}
//Called when Sign Up button is clicked
/**
* Called when Sign Up button is clicked.
* @param view View
*/
public void signUp(View view) {
Intent intent = new Intent(this, SignupActivity.class);
startActivity(intent);
}
public void askUserForTwoFactorAuth() {
twoFactorEdit.setVisibility(View.VISIBLE);
showUserToastAndCancelDialog( R.string.login_failed_2fa_needed );
}
public void showUserToastAndCancelDialog( int resId ) {
showUserToast( resId );
progressDialog.cancel();
}
private void showUserToast( int resId ) {
Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show();
}
public void showSuccessToastAndDismissDialog() {
Toast successToast = Toast.makeText(this, R.string.login_success, Toast.LENGTH_SHORT);
successToast.show();
progressDialog.dismiss();
}
public void emptySensitiveEditFields() {
passwordEdit.setText("");
twoFactorEdit.setText("");
}
}

View file

@ -0,0 +1,145 @@
package fr.free.nrw.commons.auth;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.EventLog;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import timber.log.Timber;
import java.io.IOException;
class LoginTask extends AsyncTask<String, String, String> {
private LoginActivity loginActivity;
private String username;
private String password;
private String twoFactorCode = "";
private CommonsApplication app;
public LoginTask(LoginActivity loginActivity, String username, String password, String twoFactorCode) {
this.loginActivity = loginActivity;
this.username = username;
this.password = password;
this.twoFactorCode = twoFactorCode;
app = (CommonsApplication) loginActivity.getApplicationContext();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
loginActivity.progressDialog = new ProgressDialog(loginActivity);
loginActivity.progressDialog.setIndeterminate(true);
loginActivity.progressDialog.setTitle(loginActivity.getString(R.string.logging_in_title));
loginActivity.progressDialog.setMessage(loginActivity.getString(R.string.logging_in_message));
loginActivity.progressDialog.setCanceledOnTouchOutside(false);
loginActivity.progressDialog.show();
}
@Override
protected String doInBackground(String... params) {
try {
if (twoFactorCode.isEmpty()) {
return app.getApi().login(username, password);
} else {
return app.getApi().login(username, password, twoFactorCode);
}
} catch (IOException e) {
// Do something better!
return "NetworkFailure";
}
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Timber.d("Login done!");
EventLog.schema(CommonsApplication.EVENT_LOGIN_ATTEMPT)
.param("username", username)
.param("result", result)
.log();
if (result.equals("PASS")) {
handlePassResult();
} else {
handleOtherResults( result );
}
}
private void handlePassResult() {
loginActivity.showSuccessToastAndDismissDialog();
Account account = new Account(username, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
boolean accountCreated = AccountManager.get(loginActivity).addAccount(account, password, null);
boolean accountCreated = AccountManager.get(loginActivity).addAccountExplicitly(account, password, null);
Bundle extras = loginActivity.getIntent().getExtras();
if (extras != null) {
Timber.d("Bundle of extras: %s", extras);
if (accountCreated) { // Pass the new account back to the account manager
AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
Bundle authResult = new Bundle();
authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username);
authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
if (response != null) {
response.onResult(authResult);
}
}
}
// FIXME: If the user turns it off, it shouldn't be auto turned back on
ContentResolver.setSyncAutomatically(account, ContributionsContentProvider.AUTHORITY, true); // Enable sync by default!
ContentResolver.setSyncAutomatically(account, ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
Intent intent = new Intent(loginActivity, ContributionsActivity.class);
loginActivity.startActivity(intent);
loginActivity.finish();
}
/**
* Match known failure message codes and provide messages
* @param result String
*/
private void handleOtherResults( String result ) {
if (result.equals("NetworkFailure")) {
// Matches NetworkFailure which is created by the doInBackground method
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_network );
} else if (result.toLowerCase().contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
// Matches nosuchuser, nosuchusershort, noname
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_username );
loginActivity.emptySensitiveEditFields();
} else if (result.toLowerCase().contains("wrongpassword".toLowerCase())) {
// Matches wrongpassword, wrongpasswordempty
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_password );
loginActivity.emptySensitiveEditFields();
} else if (result.toLowerCase().contains("throttle".toLowerCase())) {
// Matches unknown throttle error codes
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_throttled );
} else if (result.toLowerCase().contains("userblocked".toLowerCase())) {
// Matches login-userblocked
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_blocked );
} else if (result.equals("2FA")) {
loginActivity.askUserForTwoFactorAuth();
} else {
// Occurs with unhandled login failure codes
Timber.d("Login failed with reason: %s", result);
loginActivity.showUserToastAndCancelDialog( R.string.login_failed_generic );
}
}
}

View file

@ -93,7 +93,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="signUp"
android:text="@string/signup"
/>