#635 - First pass at launching the background task when authenticating

Began disabling the password input and unlock buttons while the task is running, for some UI feedback. Right now it's just waiting for the task to finish if the activity gets paused while it's running; this isn't great since there's potential there to lock the main thread, but the task is so short-lived that it may not be an issue.
This commit is contained in:
Joshua Soberg 2021-01-27 22:00:32 -05:00
parent 8c8de98763
commit 1780d24955

View file

@ -28,10 +28,11 @@ import android.os.Bundle;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
@ -42,28 +43,30 @@ import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.shadowice.flocke.andotp.R;
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask;
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask.Callback;
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask.Result;
import org.shadowice.flocke.andotp.Utilities.Constants;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper.PBKDF2Credentials;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
public class AuthenticateActivity extends ThemedActivity
implements EditText.OnEditorActionListener, View.OnClickListener {
private static final String KEY_WAS_TASK_ACTIVE = "AuthenticateActivity.WasTaskActive";
private AuthMethod authMethod;
private String newEncryption = "";
private String existingAuthCredentials;
private boolean isAuthUpgrade = false;
private TextInputEditText passwordInput;
private Button unlockButton;
private AuthenticationTask activeTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -146,78 +149,76 @@ public class AuthenticateActivity extends ThemedActivity
}
private void initUnlockButtonView(View v) {
Button unlockButton = v.findViewById(R.id.buttonUnlock);
unlockButton = v.findViewById(R.id.buttonUnlock);
unlockButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
checkPassword(passwordInput.getText().toString());
startAuthTask(passwordInput.getText().toString());
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
checkPassword(v.getText().toString());
startAuthTask(v.getText().toString());
return true;
}
return false;
}
public void checkPassword(String plainPassword) {
if (!isAuthUpgrade) {
try {
PBKDF2Credentials credentials = EncryptionHelper.generatePBKDF2Credentials(plainPassword, settings.getSalt(), settings.getIterations());
byte[] passwordArray = Base64.decode(existingAuthCredentials, Base64.URL_SAFE);
if (Arrays.equals(passwordArray, credentials.password)) {
finishWithResult(true, credentials.key);
} else {
finishWithResult(false, null);
private void startAuthTask(String plainPassword) {
passwordInput.setEnabled(false);
unlockButton.setEnabled(false);
Callback callback = this::handleResult;
activeTask = new AuthenticationTask(this, callback, isAuthUpgrade, existingAuthCredentials, plainPassword);
activeTask.execute();
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e) {
e.printStackTrace();
finishWithResult(false, null);
}
} else {
String hashedPassword = new String(Hex.encodeHex(DigestUtils.sha256(plainPassword)));
if (hashedPassword.equals(existingAuthCredentials)) {
byte[] key = settings.setAuthCredentials(plainPassword);
if (key == null)
private void handleResult(Result result) {
activeTask = null;
if (result.authUpgradeFailed) {
Toast.makeText(this, R.string.settings_toast_auth_upgrade_failed, Toast.LENGTH_LONG).show();
if (authMethod == AuthMethod.PASSWORD)
settings.removeAuthPasswordHash();
else if (authMethod == AuthMethod.PIN)
settings.removeAuthPINHash();
finishWithResult(true, key);
} else {
finishWithResult(false, null);
}
}
finishWithResult(result.encryptionKey != null, result.encryptionKey);
}
// End with a result
public void finishWithResult(boolean success, byte[] key) {
private void finishWithResult(boolean success, byte[] encryptionKey) {
Intent data = new Intent();
if (newEncryption != null && !newEncryption.isEmpty())
data.putExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION, newEncryption);
if (key != null)
data.putExtra(Constants.EXTRA_AUTH_PASSWORD_KEY, key);
if (encryptionKey != null)
data.putExtra(Constants.EXTRA_AUTH_PASSWORD_KEY, encryptionKey);
if (success)
setResult(RESULT_OK, data);
finish();
}
// Go back to the main activity
@Override
protected void onPause() {
super.onPause();
completeTaskIfActive();
}
/** @return true if the task was active and was completed, false otherwise. */
private boolean completeTaskIfActive() {
try {
// This will cause the main thread to wait, but it'll ensure that the task completes.
if (activeTask != null) {
handleResult(activeTask.get());
return true;
}
} catch (ExecutionException | InterruptedException e) {
Log.e("AuthenticateActivity", "Could not finish active authentication task", e);
}
return false;
}
@Override
public void onBackPressed() {
if (!completeTaskIfActive()) {
finishWithResult(false, null);
}
super.onBackPressed();
}
}