diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java index ea35edaa..1a4c4b67 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java @@ -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; @@ -57,14 +56,16 @@ 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"; - + private AuthMethod authMethod; private String newEncryption = ""; private String existingAuthCredentials; @@ -72,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; @@ -288,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(); @@ -298,6 +307,12 @@ public class AuthenticateActivity extends BaseActivity } } + @Override + protected void onStop() { + passwordInput.setAutoFillTextListener(null); + super.onStop(); + } + @Override protected void onDestroy() { ProcessLifecycleOwner.get().getLifecycle() diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java index 236bb05d..e4453296 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java @@ -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 diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java index c8853c3e..d351a3dc 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/Settings.java @@ -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)); } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/AutoFillable/AutoFillableTextInputEditText.java b/app/src/main/java/org/shadowice/flocke/andotp/View/AutoFillable/AutoFillableTextInputEditText.java new file mode 100644 index 00000000..b9c8e3f6 --- /dev/null +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/AutoFillable/AutoFillableTextInputEditText.java @@ -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); + } +} diff --git a/app/src/main/res/layout/content_authenticate.xml b/app/src/main/res/layout/content_authenticate.xml index 0075336f..4d2c22e9 100644 --- a/app/src/main/res/layout/content_authenticate.xml +++ b/app/src/main/res/layout/content_authenticate.xml @@ -24,7 +24,7 @@ android:hint="@string/auth_hint_password" app:endIconMode="password_toggle" > - pref_auth_inactivity_timeout pref_block_accessibility pref_block_autofill + pref_auto_unlock_after_autofill pref_lang pref_locale diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml index 0c5e2912..4642fc46 100644 --- a/app/src/main/res/values/strings_settings.xml +++ b/app/src/main/res/values/strings_settings.xml @@ -21,6 +21,7 @@ Delay for inactivity re-lock Block accessibility services Block autofill + Unlock automatically after autofill Language Theme @@ -74,7 +75,9 @@ services. DO NOT enable this if you rely on the accessibility services! Block autofill services from accessing password fields inside the app - This feature requires an Android version + Unlock if the password has been filled + in by autofill + This feature requires an Android version above 8.0 (Oreo) Scroll overlong labels instead of truncating them diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5c6d87eb..5f0c04fa 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -74,6 +74,12 @@ android:summary="@string/settings_desc_block_autofill" android:defaultValue="false" /> + +