Merge branch 'master' into feature/BackupsPasswdLengthCheck
This commit is contained in:
commit
1963cfbda5
13 changed files with 185 additions and 10 deletions
|
@ -27,7 +27,6 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -56,11 +55,14 @@ import org.shadowice.flocke.andotp.R;
|
|||
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask;
|
||||
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask.Result;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.EditorActionHelper;
|
||||
import org.shadowice.flocke.andotp.View.AutoFillable.AutoFillableTextInputEditText;
|
||||
|
||||
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
|
||||
|
||||
public class AuthenticateActivity extends BaseActivity
|
||||
implements EditText.OnEditorActionListener, View.OnClickListener {
|
||||
private final AutoFillableTextInputEditText.AutoFillTextListener autoFillTextListener = text -> startAuthTask(text.toString());
|
||||
|
||||
private static final String TAG_TASK_FRAGMENT = "AuthenticateActivity.TaskFragmentTag";
|
||||
|
||||
|
@ -71,7 +73,7 @@ public class AuthenticateActivity extends BaseActivity
|
|||
private ProcessLifecycleObserver observer;
|
||||
|
||||
private TextInputLayout passwordLayout;
|
||||
private TextInputEditText passwordInput;
|
||||
AutoFillableTextInputEditText passwordInput;
|
||||
private Button unlockButton;
|
||||
private ProgressBar unlockProgress;
|
||||
|
||||
|
@ -237,7 +239,7 @@ public class AuthenticateActivity extends BaseActivity
|
|||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
if (EditorActionHelper.isActionDoneOrKeyboardEnter(actionId, event)) {
|
||||
startAuthTask(v.getText().toString());
|
||||
return true;
|
||||
}
|
||||
|
@ -287,6 +289,14 @@ public class AuthenticateActivity extends BaseActivity
|
|||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (settings.getAutoUnlockAfterAutofill()) {
|
||||
passwordInput.setAutoFillTextListener(autoFillTextListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
@ -297,6 +307,12 @@ public class AuthenticateActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
passwordInput.setAutoFillTextListener(null);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
ProcessLifecycleOwner.get().getLifecycle()
|
||||
|
|
|
@ -32,6 +32,7 @@ import android.text.InputType;
|
|||
import android.text.TextWatcher;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -57,6 +58,7 @@ import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
|||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.ConfirmedPasswordTransformationHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.EditorActionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||
import org.shadowice.flocke.andotp.Utilities.UIHelper;
|
||||
|
||||
|
@ -266,7 +268,7 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
}
|
||||
}
|
||||
|
||||
public static class AuthenticationFragment extends SlideFragment {
|
||||
public static class AuthenticationFragment extends SlideFragment implements TextView.OnEditorActionListener {
|
||||
private Constants.EncryptionType encryptionType = Constants.EncryptionType.KEYSTORE;
|
||||
|
||||
private int slidePos = -1;
|
||||
|
@ -275,6 +277,7 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
private String lengthWarning = "";
|
||||
private String noPasswordWarning = "";
|
||||
private String confirmPasswordWarning = "";
|
||||
private String passwordMismatchWarning = "";
|
||||
|
||||
private TextView desc = null;
|
||||
private Spinner selection = null;
|
||||
|
@ -432,6 +435,7 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
lengthWarning = getString(R.string.settings_label_short_password, minLength);
|
||||
noPasswordWarning = getString(R.string.intro_slide3_warn_no_password);
|
||||
confirmPasswordWarning = getString(R.string.intro_slide3_warn_confirm_password);
|
||||
passwordMismatchWarning = getString(R.string.intro_slide3_warn_password_mismatch);
|
||||
|
||||
focusOnPasswordInput();
|
||||
}
|
||||
|
@ -458,6 +462,7 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
lengthWarning = getString(R.string.settings_label_short_pin, minLength);
|
||||
noPasswordWarning = getString(R.string.intro_slide3_warn_no_pin);
|
||||
confirmPasswordWarning = getString(R.string.intro_slide3_warn_confirm_pin);
|
||||
passwordMismatchWarning = getString(R.string.intro_slide3_warn_pin_mismatch);
|
||||
|
||||
focusOnPasswordInput();
|
||||
}
|
||||
|
@ -485,11 +490,27 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
passwordInput.addTextChangedListener(textWatcher);
|
||||
passwordConfirm.addTextChangedListener(textWatcher);
|
||||
|
||||
passwordConfirm.setOnEditorActionListener(this);
|
||||
|
||||
selection.setSelection(selectionMapping.indexOfValue(Constants.AuthMethod.PASSWORD));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (EditorActionHelper.isActionDoneOrKeyboardEnter(actionId, event)) {
|
||||
nextSlide();
|
||||
return true;
|
||||
} else if (EditorActionHelper.isActionUpKeyboardEnter(event)) {
|
||||
// Ignore action up after keyboard enter. Otherwise the go-back button would be selected
|
||||
// after pressing enter with an invalid password.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGoForward() {
|
||||
Constants.AuthMethod authMethod = selectionMapping.get(selection.getSelectedItemPosition());
|
||||
|
@ -506,6 +527,9 @@ public class IntroScreenActivity extends IntroActivity {
|
|||
if (! confirm.isEmpty() && confirm.equals(password)) {
|
||||
hideWarning();
|
||||
return true;
|
||||
} else if (! confirm.isEmpty() && ! confirm.equals(password)) {
|
||||
updateWarning(passwordMismatchWarning);
|
||||
return false;
|
||||
} else {
|
||||
updateWarning(confirmPasswordWarning);
|
||||
return false;
|
||||
|
|
|
@ -363,12 +363,17 @@ public class SettingsActivity extends BaseActivity
|
|||
});
|
||||
|
||||
CheckBoxPreference blockAutofill = (CheckBoxPreference) findPreference(getString(R.string.settings_key_block_autofill));
|
||||
CheckBoxPreference autoUnlockAfterAutofill = (CheckBoxPreference) findPreference(getString(R.string.settings_key_auto_unlock_after_autofill));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
blockAutofill.setEnabled(true);
|
||||
blockAutofill.setSummary(R.string.settings_desc_block_autofill);
|
||||
autoUnlockAfterAutofill.setEnabled(true);
|
||||
autoUnlockAfterAutofill.setSummary(R.string.settings_desc_auto_unlock_after_autofill);
|
||||
} else {
|
||||
blockAutofill.setEnabled(false);
|
||||
blockAutofill.setSummary(R.string.settings_desc_block_autofill_android);
|
||||
blockAutofill.setSummary(R.string.settings_desc_autofill_requires_android_o);
|
||||
autoUnlockAfterAutofill.setEnabled(false);
|
||||
autoUnlockAfterAutofill.setSummary(R.string.settings_desc_autofill_requires_android_o);
|
||||
}
|
||||
|
||||
// Authentication
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.os.Build;
|
|||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
@ -18,10 +19,11 @@ import android.widget.TextView;
|
|||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.ConfirmedPasswordTransformationHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.EditorActionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Tools;
|
||||
|
||||
public class PasswordEntryDialog extends AppCompatDialog
|
||||
implements View.OnClickListener, TextWatcher {
|
||||
implements View.OnClickListener, TextWatcher, TextView.OnEditorActionListener {
|
||||
|
||||
public enum Mode { ENTER, UPDATE }
|
||||
|
||||
|
@ -75,8 +77,10 @@ public class PasswordEntryDialog extends AppCompatDialog
|
|||
passwordConfirm.setVisibility(View.VISIBLE);
|
||||
passwordInput.addTextChangedListener(this);
|
||||
passwordConfirm.addTextChangedListener(this);
|
||||
passwordConfirm.setOnEditorActionListener(this);
|
||||
} else if (this.dialogMode == Mode.ENTER) {
|
||||
passwordConfirm.setVisibility(View.GONE);
|
||||
passwordInput.setOnEditorActionListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,6 +100,20 @@ public class PasswordEntryDialog extends AppCompatDialog
|
|||
public void afterTextChanged(Editable s) {}
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (EditorActionHelper.isActionDoneOrKeyboardEnter(actionId, event)) {
|
||||
if (okButton.isEnabled()) okButton.performClick();
|
||||
return true;
|
||||
} else if (EditorActionHelper.isActionUpKeyboardEnter(event)) {
|
||||
// Ignore action up after keyboard enter. Otherwise the cancel button would be selected
|
||||
// after pressing enter with an invalid password.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// View.OnClickListener
|
||||
public void onClick(View view) {
|
||||
if (view.getId() == R.id.buttonOk) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.text.Editable;
|
|||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
@ -47,6 +48,7 @@ import com.google.android.material.textfield.TextInputLayout;
|
|||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.ConfirmedPasswordTransformationHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.EditorActionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||
import org.shadowice.flocke.andotp.Utilities.UIHelper;
|
||||
|
||||
|
@ -59,7 +61,7 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
|
|||
import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType;
|
||||
|
||||
public class CredentialsPreference extends DialogPreference
|
||||
implements AdapterView.OnItemClickListener, View.OnClickListener, TextWatcher {
|
||||
implements AdapterView.OnItemClickListener, View.OnClickListener, TextWatcher, TextView.OnEditorActionListener {
|
||||
public static final AuthMethod DEFAULT_VALUE = AuthMethod.NONE;
|
||||
|
||||
public interface EncryptionChangeCallback {
|
||||
|
@ -143,6 +145,7 @@ public class CredentialsPreference extends DialogPreference
|
|||
|
||||
passwordInput.addTextChangedListener(this);
|
||||
passwordConfirm.addTextChangedListener(this);
|
||||
passwordConfirm.setOnEditorActionListener(this);
|
||||
|
||||
Button btnCancel = view.findViewById(R.id.btnCancel);
|
||||
btnSave = view.findViewById(R.id.btnSave);
|
||||
|
@ -290,6 +293,20 @@ public class CredentialsPreference extends DialogPreference
|
|||
passwordConfirm.setText(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (EditorActionHelper.isActionDoneOrKeyboardEnter(actionId, event)) {
|
||||
if (btnSave.isEnabled()) btnSave.performClick();
|
||||
return true;
|
||||
} else if (EditorActionHelper.isActionUpKeyboardEnter(event)) {
|
||||
// Ignore action up after keyboard enter. Otherwise the cancel button would be selected
|
||||
// after pressing enter with an invalid password.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Needed stub functions
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.shadowice.flocke.andotp.Utilities;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import static android.view.KeyEvent.ACTION_DOWN;
|
||||
import static android.view.KeyEvent.ACTION_UP;
|
||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||
import static android.view.KeyEvent.KEYCODE_NUMPAD_ENTER;
|
||||
|
||||
public class EditorActionHelper {
|
||||
|
||||
private EditorActionHelper() { /* not allowed */ }
|
||||
|
||||
public static boolean isActionDoneOrKeyboardEnter(int actionId, KeyEvent event) {
|
||||
return actionId == EditorInfo.IME_ACTION_DONE
|
||||
|| event.getAction() == ACTION_DOWN && (event.getKeyCode() == KEYCODE_ENTER || event.getKeyCode() == KEYCODE_NUMPAD_ENTER);
|
||||
}
|
||||
|
||||
public static boolean isActionUpKeyboardEnter(KeyEvent event) {
|
||||
return event.getAction() == ACTION_UP && (event.getKeyCode() == KEYCODE_ENTER || event.getKeyCode() == KEYCODE_NUMPAD_ENTER);
|
||||
}
|
||||
}
|
|
@ -606,6 +606,10 @@ public class Settings {
|
|||
return getBoolean(R.string.settings_key_block_autofill, false);
|
||||
}
|
||||
|
||||
public boolean getAutoUnlockAfterAutofill() {
|
||||
return getBoolean(R.string.settings_key_auto_unlock_after_autofill, false);
|
||||
}
|
||||
|
||||
public void setDefaultBackupType(Constants.BackupType type) {
|
||||
setString(R.string.settings_key_backup_default_type, type.name().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package org.shadowice.flocke.andotp.View.AutoFillable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.autofill.AutofillValue;
|
||||
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* {@link TextInputEditText} that allows setting a {@link AutoFillTextListener} for getting notified when
|
||||
* this field was filled in by autofill.
|
||||
*
|
||||
* On devices running Android 7.1 (API 25) and below the listener will never be called, because
|
||||
* autofill is not supported.
|
||||
*/
|
||||
public class AutoFillableTextInputEditText extends TextInputEditText {
|
||||
|
||||
@Nullable private AutoFillTextListener listener = null;
|
||||
|
||||
public AutoFillableTextInputEditText(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AutoFillableTextInputEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AutoFillableTextInputEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void autofill(AutofillValue value) {
|
||||
super.autofill(value);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value != null && value.isText()) {
|
||||
listener.onTextAutoFilled(value.getTextValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoFillTextListener(@Nullable AutoFillTextListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public interface AutoFillTextListener {
|
||||
|
||||
void onTextAutoFilled(@NonNull CharSequence text);
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@
|
|||
android:hint="@string/auth_hint_password"
|
||||
app:endIconMode="password_toggle" >
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
<org.shadowice.flocke.andotp.View.AutoFillable.AutoFillableTextInputEditText
|
||||
android:id="@+id/passwordEdit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<string name="settings_key_auth_inactivity_delay" translatable="false">pref_auth_inactivity_timeout</string>
|
||||
<string name="settings_key_block_accessibility" translatable="false">pref_block_accessibility</string>
|
||||
<string name="settings_key_block_autofill" translatable="false">pref_block_autofill</string>
|
||||
<string name="settings_key_auto_unlock_after_autofill" translatable="false">pref_auto_unlock_after_autofill</string>
|
||||
|
||||
<string name="settings_key_lang" translatable="false">pref_lang</string>
|
||||
<string name="settings_key_locale" translatable="false">pref_locale</string> <!-- Deprecated -->
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
<string name="intro_slide3_warn_no_password">Please set a password to continue!</string>
|
||||
<string name="intro_slide3_warn_no_pin">Please set a PIN to continue!</string>
|
||||
<string name="intro_slide3_warn_confirm_password">Please confirm your password to continue!</string>
|
||||
<string name="intro_slide3_warn_password_mismatch">Passwords must match!</string>
|
||||
<string name="intro_slide3_warn_confirm_pin">Please confirm your PIN to continue!</string>
|
||||
<string name="intro_slide3_warn_pin_mismatch">Pins must match!</string>
|
||||
|
||||
<string name="intro_slide4_title">Finished</string>
|
||||
<string name="intro_slide4_desc">Your settings have been saved, you are now all set to use
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
<string name="settings_title_auth_inactivity_delay">Delay for inactivity re-lock</string>
|
||||
<string name="settings_title_block_accessibility">Block accessibility services</string>
|
||||
<string name="settings_title_block_autofill">Block autofill</string>
|
||||
<string name="settings_title_auto_unlock_after_autofill">Unlock automatically after autofill</string>
|
||||
|
||||
<string name="settings_title_lang">Language</string>
|
||||
<string name="settings_title_theme">Theme</string>
|
||||
|
@ -74,7 +75,9 @@
|
|||
services. <b>DO NOT enable this if you rely on the accessibility services!</b></string>
|
||||
<string name="settings_desc_block_autofill">Block autofill services from accessing password
|
||||
fields inside the app</string>
|
||||
<string name="settings_desc_block_autofill_android">This feature requires an Android version
|
||||
<string name="settings_desc_auto_unlock_after_autofill">Unlock if the password has been filled
|
||||
in by autofill</string>
|
||||
<string name="settings_desc_autofill_requires_android_o">This feature requires an Android version
|
||||
above 8.0 (Oreo)</string>
|
||||
|
||||
<string name="settings_desc_label_scroll">Scroll overlong labels instead of truncating them</string>
|
||||
|
|
|
@ -74,6 +74,12 @@
|
|||
android:summary="@string/settings_desc_block_autofill"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="@string/settings_key_auto_unlock_after_autofill"
|
||||
android:title="@string/settings_title_auto_unlock_after_autofill"
|
||||
android:summary="@string/settings_desc_auto_unlock_after_autofill"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
|
Loading…
Reference in a new issue