Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable autolock delay #6

Merged
merged 5 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -68,6 +72,13 @@ class ConfigurationActivity : Activity() {
lockStore.isAutoLockEnabled = isChecked
}

val autoLockDelayMinutesEditable = findViewById<EditText>(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
Expand Down Expand Up @@ -120,4 +131,4 @@ class ConfigurationActivity : Activity() {
},
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
29 changes: 25 additions & 4 deletions app/src/main/java/alt/nainapps/aer/lock/LockStore.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021 2bllw8
* Copyright (c) 2024 nain
* SPDX-License-Identifier: GPL-3.0-only
*/
package alt.nainapps.aer.lock
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()
)
}
Expand All @@ -166,24 +183,28 @@ 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"

private const val LOCK_PREFERENCES = "lock_store"
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

Expand Down
12 changes: 6 additions & 6 deletions app/src/main/java/alt/nainapps/aer/lock/UnlockActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -50,7 +50,7 @@ class UnlockActivity : Activity() {
val unlockBtn = findViewById<Button>(R.id.unlockButton)
val cancelBtn = findViewById<Button>(R.id.cancelButton)

passwordField.addTextChangedListener(TextListener { text: String? ->
passwordField.addTextChangedListener(PasswordTextListener { text: String? ->
unlockBtn.isEnabled = passwordField.text.length >= MIN_PASSWORD_LENGTH
})

Expand Down
38 changes: 34 additions & 4 deletions app/src/main/res/layout/configuration.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<!--
~ Copyright (c) 2022 2bllw8
~ Copyright (c) 2024 nain
~ SPDX-License-Identifier: GPL-3.0-only
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
Expand Down Expand Up @@ -56,23 +57,52 @@
tools:text="@string/configuration_password_set" />

<Switch
android:id="@+id/configuration_auto_lock"
android:id="@+id/configuration_biometric_unlock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:minHeight="56dp"
android:paddingHorizontal="16dp"
android:text="@string/configuration_storage_lock_auto"
android:text="@string/configuration_storage_unlock_biometric"
android:textSize="16sp" />

<Switch
android:id="@+id/configuration_biometric_unlock"
android:id="@+id/configuration_auto_lock"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:minHeight="56dp"
android:paddingHorizontal="16dp"
android:text="@string/configuration_storage_unlock_biometric"
android:text="@string/configuration_storage_lock_auto"
android:textSize="16sp" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">

<TextView
android:id="@+id/configuration_auto_lock_delay_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/config_auto_lock_delay_minutes"
android:paddingHorizontal="16dp"
android:text="@string/configuration_auto_lock_delay_minutes_label"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
tools:text="@string/configuration_auto_lock_delay_minutes_label" />

<EditText
android:id="@+id/config_auto_lock_delay_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autofillHints="autoLockDelayMinutes"
android:minHeight="56dp"
android:minEms="3"
android:maxEms="5"
android:inputType="number" />
</LinearLayout>


</LinearLayout>
</ScrollView>
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@
<string name="configuration_storage_lock">Lock storage access</string>
<string name="configuration_storage_unlock">Unlock storage access</string>
<string name="configuration_storage_unlock_biometric">Allow storage unlock with biometric access</string>
<string name="configuration_storage_lock_auto">Automatically lock access after 15 minutes</string>
<string name="configuration_storage_lock_auto">Automatically lock access after delay</string>
<string name="title_activity_storage_pref">StoragePrefActivity</string>
<string name="shortcut_storageconfig_long">Storage Configuration</string>
<string name="shortcut_storageconfig_short">Storage Config</string>
<string name="shortcut_storageconfig_disabled">Storage Config (Disabled)</string>
<string name="storage_config_select_help">Tap to select / Drag to reorder:</string>
<string name="storage_config_screen_title">Aer Storage Backend</string>
<string name="configuration_auto_lock_delay_minutes_label">Auto lock delay (in minutes)</string>

</resources>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ if (file('local.properties').exists()) {

ext {
minSdkVersion = 26
targetSdkVersion = 34
targetSdkVersion = 35

sourceCompatibilityVersion = JavaVersion.VERSION_17
targetCompatibilityVersion = JavaVersion.VERSION_17
Expand Down
10 changes: 5 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[versions]
annotation = "1.8.2"
coreKtx = "1.13.1"
annotation = "1.9.1"
coreKtx = "1.15.0"
eitherLib = "3.4.0"
agp = "8.6.1"
kotlin = "2.0.20"
exifinterface = "1.3.7"
lifecycleRuntimeKtx = "2.8.6"
activityCompose = "1.9.2"
composeBom = "2024.09.02"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.11.00"
reorderable = "2.3.3"


Expand Down
2 changes: 1 addition & 1 deletion metadata/en-US/full_description.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Features
- The system Files app is also used as file picker, so you can pick Aer files to share
- 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 into Aer using the Android share functionality

Expand Down
Loading