diff --git a/README.md b/README.md index 2210db4..cf6b0f1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Moreover it provides ways for the user to export contents from other apps and sa - An optional shortcut for devices that do not expose the system Files app is offered - Lock access to the private storage - Quick tile - - Auto lock after 15 minutes + - **Auto lock after set delay** - Password for locking access to the files - Import content using the share Android functionality - **Option to select which private storage location to use** diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 17d1edc..f304a66 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { defaultConfig { minSdk = rootProject.extra["minSdkVersion"] as Int targetSdk = rootProject.extra["targetSdkVersion"] as Int - versionCode = 1730403000 - versionName = "2024.10.31" + versionCode = 1733570000 + versionName = "2024.12.07" applicationId = "alt.nainapps.aer" vectorDrawables { useSupportLibrary = true diff --git a/app/src/main/java/alt/nainapps/aer/config/ConfigurationActivity.kt b/app/src/main/java/alt/nainapps/aer/config/ConfigurationActivity.kt index 6d9df7c..e2a9326 100644 --- a/app/src/main/java/alt/nainapps/aer/config/ConfigurationActivity.kt +++ b/app/src/main/java/alt/nainapps/aer/config/ConfigurationActivity.kt @@ -1,10 +1,12 @@ /* * Copyright (c) 2022 2bllw8 + * Copyright (c) 2024 nain * SPDX-License-Identifier: GPL-3.0-only */ package alt.nainapps.aer.config import alt.nainapps.aer.R +import alt.nainapps.aer.config.autolock.buildValidatedAutoLockDelayListener import alt.nainapps.aer.config.password.ChangePasswordDialog import alt.nainapps.aer.config.password.SetPasswordDialog import alt.nainapps.aer.lock.LockStore @@ -13,8 +15,10 @@ import alt.nainapps.aer.shell.AnemoShell import android.app.Activity import android.content.Intent import android.os.Bundle +import android.text.Editable import android.view.View import android.widget.CompoundButton +import android.widget.EditText import android.widget.Switch import android.widget.TextView import java.util.function.Consumer @@ -68,6 +72,13 @@ class ConfigurationActivity : Activity() { lockStore.isAutoLockEnabled = isChecked } + val autoLockDelayMinutesEditable = findViewById(R.id.config_auto_lock_delay_minutes) + autoLockDelayMinutesEditable.text = Editable.Factory.getInstance().newEditable( + lockStore.autoLockDelayMinutes.toString() + ) + val autoLockDelayListener = buildValidatedAutoLockDelayListener(this.baseContext, lockStore, autoLockDelayMinutesEditable) + autoLockDelayMinutesEditable.addTextChangedListener(autoLockDelayListener) + biometricSwitch = findViewById(R.id.configuration_biometric_unlock) biometricSwitch!!.visibility = if (lockStore.canAuthenticateBiometric()) View.VISIBLE else View.GONE biometricSwitch!!.isChecked = lockStore.isBiometricUnlockEnabled @@ -120,4 +131,4 @@ class ConfigurationActivity : Activity() { }, ) } -} +} \ No newline at end of file diff --git a/app/src/main/java/alt/nainapps/aer/config/autolock/AutoLockDelayMinutesTextListener.kt b/app/src/main/java/alt/nainapps/aer/config/autolock/AutoLockDelayMinutesTextListener.kt new file mode 100644 index 0000000..9c81e0a --- /dev/null +++ b/app/src/main/java/alt/nainapps/aer/config/autolock/AutoLockDelayMinutesTextListener.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 nain + * SPDX-License-Identifier: GPL-3.0-only + */ + +package alt.nainapps.aer.config.autolock + +import alt.nainapps.aer.R +import alt.nainapps.aer.lock.LockStore +import android.content.Context +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText + +fun interface AutoLockDelayMinutesTextListener : TextWatcher { + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + } + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + } + override fun afterTextChanged(s: Editable) { + afterTextChanged(s.toString()) + } + // this will be provided as lambda + fun afterTextChanged(text: String) +} + +fun buildValidatedAutoLockDelayListener(context: Context, lockstore: LockStore, input: EditText): AutoLockDelayMinutesTextListener { + return AutoLockDelayMinutesTextListener { text -> + // validate text isNumber + try { + text.toLong() + } catch (error: NumberFormatException) { + input.setError("NaN: Not a Number", + context.getDrawable(R.drawable.ic_error)) + return@AutoLockDelayMinutesTextListener + } + val minutes: Long = text.toLong() + // validate not zero + if (minutes == 0L) { + input.setError("Auto lock delay can't be zero", + context.getDrawable(R.drawable.ic_error)) + } + lockstore.autoLockDelayMinutes = minutes + } +} \ No newline at end of file diff --git a/app/src/main/java/alt/nainapps/aer/config/password/ChangePasswordDialog.kt b/app/src/main/java/alt/nainapps/aer/config/password/ChangePasswordDialog.kt index 87b5142..cdbb77f 100644 --- a/app/src/main/java/alt/nainapps/aer/config/password/ChangePasswordDialog.kt +++ b/app/src/main/java/alt/nainapps/aer/config/password/ChangePasswordDialog.kt @@ -57,8 +57,8 @@ class ChangePasswordDialog(activity: Activity, lockStore: LockStore, onSuccess: private fun buildTextListener( passwordField: EditText, repeatField: EditText, positiveBtn: Button - ): TextListener { - return TextListener { + ): PasswordTextListener { + return PasswordTextListener { val passwordValue = passwordField.text.toString() val repeatValue = repeatField.text.toString() if (passwordValue.length < MIN_PASSWORD_LENGTH) { diff --git a/app/src/main/java/alt/nainapps/aer/config/password/TextListener.kt b/app/src/main/java/alt/nainapps/aer/config/password/PasswordTextListener.kt similarity index 90% rename from app/src/main/java/alt/nainapps/aer/config/password/TextListener.kt rename to app/src/main/java/alt/nainapps/aer/config/password/PasswordTextListener.kt index 8e56a4f..6ef56e1 100644 --- a/app/src/main/java/alt/nainapps/aer/config/password/TextListener.kt +++ b/app/src/main/java/alt/nainapps/aer/config/password/PasswordTextListener.kt @@ -7,7 +7,7 @@ package alt.nainapps.aer.config.password import android.text.Editable import android.text.TextWatcher -fun interface TextListener : TextWatcher { +fun interface PasswordTextListener : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { } diff --git a/app/src/main/java/alt/nainapps/aer/config/password/SetPasswordDialog.kt b/app/src/main/java/alt/nainapps/aer/config/password/SetPasswordDialog.kt index 71a5f41..3f8f5fd 100644 --- a/app/src/main/java/alt/nainapps/aer/config/password/SetPasswordDialog.kt +++ b/app/src/main/java/alt/nainapps/aer/config/password/SetPasswordDialog.kt @@ -42,8 +42,8 @@ class SetPasswordDialog(activity: Activity, lockStore: LockStore, onSuccess: Run private fun buildValidator( passwordField: EditText, repeatField: EditText, positiveBtn: Button - ): TextListener { - return TextListener { + ): PasswordTextListener { + return PasswordTextListener { val passwordValue = passwordField.text.toString() val repeatValue = repeatField.text.toString() if (passwordValue.length < MIN_PASSWORD_LENGTH) { diff --git a/app/src/main/java/alt/nainapps/aer/lock/LockStore.kt b/app/src/main/java/alt/nainapps/aer/lock/LockStore.kt index 097e53c..2ee4293 100644 --- a/app/src/main/java/alt/nainapps/aer/lock/LockStore.kt +++ b/app/src/main/java/alt/nainapps/aer/lock/LockStore.kt @@ -1,5 +1,6 @@ /* * Copyright (c) 2021 2bllw8 + * Copyright (c) 2024 nain * SPDX-License-Identifier: GPL-3.0-only */ package alt.nainapps.aer.lock @@ -114,6 +115,22 @@ class LockStore private constructor(context: Context) : OnSharedPreferenceChange } } + @get:Synchronized + @set:Synchronized + var autoLockDelayMinutes: Long + get() = preferences.getLong(KEY_AUTO_LOCK_DELAY_MINUTES, DEFAULT_AUTO_LOCK_DELAY_MINUTES) + set(delayMinutes) { + preferences.edit().putLong(KEY_AUTO_LOCK_DELAY_MINUTES, delayMinutes).apply() + + if (!isLocked) { + if (isAutoLockEnabled) { + // If auto-lock is enabled while the storage is unlocked, schedule new job + cancelAutoLock() + scheduleAutoLock() + } + } + } + fun canAuthenticateBiometric(): Boolean { return Build.VERSION.SDK_INT >= 29 && biometricManager != null && biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS } @@ -146,7 +163,7 @@ class LockStore private constructor(context: Context) : OnSharedPreferenceChange private fun scheduleAutoLock() { jobScheduler.schedule( JobInfo.Builder(AUTO_LOCK_JOB_ID, autoLockComponent) - .setMinimumLatency(AUTO_LOCK_DELAY) + .setMinimumLatency(millisFromMinutes(autoLockDelayMinutes)) .build() ) } @@ -166,6 +183,11 @@ class LockStore private constructor(context: Context) : OnSharedPreferenceChange } } + // convert minutes to milliseconds + private fun millisFromMinutes(minutes: Long): Long { + return minutes * 60L * 1000L + } + companion object { private const val TAG = "LockStore" @@ -173,17 +195,16 @@ class LockStore private constructor(context: Context) : OnSharedPreferenceChange private const val KEY_LOCK = "is_locked" private const val KEY_PASSWORD = "password_hash" private const val KEY_AUTO_LOCK = "auto_lock" + private const val KEY_AUTO_LOCK_DELAY_MINUTES = "auto_lock_delay_minutes" private const val KEY_BIOMETRIC_UNLOCK = "biometric_unlock" private const val DEFAULT_LOCK_VALUE = false private const val DEFAULT_AUTO_LOCK_VALUE = false + private const val DEFAULT_AUTO_LOCK_DELAY_MINUTES = 15L private const val HASH_ALGORITHM = "SHA-256" private const val AUTO_LOCK_JOB_ID = 64 - // 15 minutes in milliseconds - private const val AUTO_LOCK_DELAY = 1000L * 60L * 15L - @Volatile private var instance: LockStore? = null diff --git a/app/src/main/java/alt/nainapps/aer/lock/UnlockActivity.kt b/app/src/main/java/alt/nainapps/aer/lock/UnlockActivity.kt index 89e1138..0001a9b 100644 --- a/app/src/main/java/alt/nainapps/aer/lock/UnlockActivity.kt +++ b/app/src/main/java/alt/nainapps/aer/lock/UnlockActivity.kt @@ -4,6 +4,11 @@ */ package alt.nainapps.aer.lock +import alt.nainapps.aer.R +import alt.nainapps.aer.config.ConfigurationActivity +import alt.nainapps.aer.config.password.PasswordTextListener +import alt.nainapps.aer.lock.LockStore.Companion.getInstance +import alt.nainapps.aer.shell.LauncherActivity import android.app.Activity import android.content.DialogInterface import android.content.Intent @@ -15,11 +20,6 @@ import android.widget.Button import android.widget.EditText import android.widget.ImageView import androidx.annotation.RequiresApi -import alt.nainapps.aer.R -import alt.nainapps.aer.config.ConfigurationActivity -import alt.nainapps.aer.config.password.TextListener -import alt.nainapps.aer.lock.LockStore.Companion.getInstance -import alt.nainapps.aer.shell.LauncherActivity class UnlockActivity : Activity() { private var lockStore: LockStore? = null @@ -50,7 +50,7 @@ class UnlockActivity : Activity() { val unlockBtn = findViewById