From 0a0b264e436054d34986fddd0963c7a1baf241ff Mon Sep 17 00:00:00 2001 From: stevenyomi <95685115+stevenyomi@users.noreply.github.com> Date: Sun, 23 Oct 2022 03:07:44 +0800 Subject: [PATCH] Fix app lock and make delay not survive app being killed (#8272) * Fix app lock * Always require unlock if app is killed (cherry picked from commit 2ab744c525e2c6c94bcac7d4cc3848830a50c991) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 4 +- .../base/delegate/SecureActivityDelegate.kt | 152 ++++++++---------- .../ui/browse/source/SourcesPresenter.kt | 1 - .../util/system/AuthenticatorUtil.kt | 15 -- 4 files changed, 65 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index c9a4c6a9a..5f05b6f6b 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -208,8 +208,8 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { }.build() } - override fun onCreate(owner: LifecycleOwner) { - SecureActivityDelegate.onApplicationCreated() + override fun onStart(owner: LifecycleOwner) { + SecureActivityDelegate.onApplicationStart() } private fun addAnalytics() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt index ba23bbc40..949d11b8e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt @@ -7,14 +7,6 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_ALL_DAYS -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_FRIDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_MONDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_SATURDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_SUNDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_THURSDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_TUESDAY -import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate.Companion.LOCK_WEDNESDAY import eu.kanade.tachiyomi.ui.category.biometric.TimeRange import eu.kanade.tachiyomi.ui.security.UnlockActivity import eu.kanade.tachiyomi.util.system.AuthenticatorUtil @@ -27,7 +19,6 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.util.Calendar -import java.util.Date import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -46,49 +37,82 @@ interface SecureActivityDelegate { const val LOCK_ALL_DAYS = 0x7F // SY <-- - fun onApplicationCreated() { - val lockDelay = Injekt.get().lockAppAfter().get() - if (lockDelay <= 0) { - // Restore always active/on start app lock - // Delayed lock will be restored later on activity resume - lockState = LockState.ACTIVE - } - } + /** + * Set to true if we need the first activity to authenticate. + * + * Always require unlock if app is killed. + */ + var requireUnlock = true fun onApplicationStopped() { val preferences = Injekt.get() if (!preferences.useAuthenticator().get()) return - if (lockState != LockState.ACTIVE) { - preferences.lastAppClosed().set(Date().time) - } + if (!AuthenticatorUtil.isAuthenticating) { - val lockAfter = preferences.lockAppAfter().get() - lockState = if (lockAfter > 0) { - LockState.PENDING - } else if (lockAfter == -1) { - // Never lock on idle - LockState.INACTIVE - } else { - LockState.ACTIVE + // Return if app is closed in locked state + if (requireUnlock) return + // Save app close time if lock is delayed + if (preferences.lockAppAfter().get() > 0) { + preferences.lastAppClosed().set(System.currentTimeMillis()) } } } + // SY --> + private fun canLockNow(preferences: SecurityPreferences): Boolean { + val today: Calendar = Calendar.getInstance() + val timeRanges = preferences.authenticatorTimeRanges().get() + .mapNotNull { TimeRange.fromPreferenceString(it) } + val canLockNow = if (timeRanges.isNotEmpty()) { + val now = today.get(Calendar.HOUR_OF_DAY).hours + today.get(Calendar.MINUTE).minutes + timeRanges.any { now in it } + } else { + true + } + + val lockedDays = preferences.authenticatorDays().get() + val canLockToday = lockedDays == LOCK_ALL_DAYS || when (today.get(Calendar.DAY_OF_WEEK)) { + Calendar.SUNDAY -> (lockedDays and LOCK_SUNDAY) == LOCK_SUNDAY + Calendar.MONDAY -> (lockedDays and LOCK_MONDAY) == LOCK_MONDAY + Calendar.TUESDAY -> (lockedDays and LOCK_TUESDAY) == LOCK_TUESDAY + Calendar.WEDNESDAY -> (lockedDays and LOCK_WEDNESDAY) == LOCK_WEDNESDAY + Calendar.THURSDAY -> (lockedDays and LOCK_THURSDAY) == LOCK_THURSDAY + Calendar.FRIDAY -> (lockedDays and LOCK_FRIDAY) == LOCK_FRIDAY + Calendar.SATURDAY -> (lockedDays and LOCK_SATURDAY) == LOCK_SATURDAY + else -> false + } + + return canLockNow && canLockToday + } + // SY <-- + + /** + * Checks if unlock is needed when app comes foreground. + */ + fun onApplicationStart() { + val preferences = Injekt.get() + if (!preferences.useAuthenticator().get()) return + + val lastClosedPref = preferences.lastAppClosed() + + // `requireUnlock` can be true on process start or if app was closed in locked state + if (!AuthenticatorUtil.isAuthenticating && !requireUnlock) { + requireUnlock = /* SY --> */ canLockNow(preferences) && /* SY <-- */ when (val lockDelay = preferences.lockAppAfter().get()) { + -1 -> false // Never + 0 -> true // Always + else -> lastClosedPref.get() + lockDelay * 60_000 <= System.currentTimeMillis() + } + } + + lastClosedPref.delete() + } + fun unlock() { - lockState = LockState.INACTIVE - Injekt.get().lastAppClosed().delete() + requireUnlock = false } } } -private var lockState = LockState.INACTIVE - -private enum class LockState { - INACTIVE, - PENDING, - ACTIVE, -} - class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObserver { private lateinit var activity: AppCompatActivity @@ -123,61 +147,11 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser private fun setAppLock() { if (!securityPreferences.useAuthenticator().get()) return if (activity.isAuthenticationSupported()) { - updatePendingLockStatus() - if (!isAppLocked()) return + if (!SecureActivityDelegate.requireUnlock) return activity.startActivity(Intent(activity, UnlockActivity::class.java)) activity.overridePendingTransition(0, 0) } else { securityPreferences.useAuthenticator().set(false) } } - - private fun updatePendingLockStatus() { - val lastClosedPref = securityPreferences.lastAppClosed() - val lockDelay = 60000 * securityPreferences.lockAppAfter().get() - if (lastClosedPref.isSet() && lockDelay > 0) { - // Restore pending status in case app was killed - lockState = LockState.PENDING - } - if (lockState != LockState.PENDING) { - return - } - if (Date().time >= lastClosedPref.get() + lockDelay) { - // Activate lock after delay - lockState = LockState.ACTIVE - } - } - - private fun isAppLocked(): Boolean { - // SY --> - val today: Calendar = Calendar.getInstance() - val timeRanges = securityPreferences.authenticatorTimeRanges().get() - .mapNotNull { TimeRange.fromPreferenceString(it) } - if (timeRanges.isNotEmpty()) { - val now = today.get(Calendar.HOUR_OF_DAY).hours + today.get(Calendar.MINUTE).minutes - val lockedNow = timeRanges.any { now in it } - if (!lockedNow) { - return false - } - } - - val lockedDays = securityPreferences.authenticatorDays().get() - val lockedToday = lockedDays == LOCK_ALL_DAYS || when (today.get(Calendar.DAY_OF_WEEK)) { - Calendar.SUNDAY -> (lockedDays and LOCK_SUNDAY) == LOCK_SUNDAY - Calendar.MONDAY -> (lockedDays and LOCK_MONDAY) == LOCK_MONDAY - Calendar.TUESDAY -> (lockedDays and LOCK_TUESDAY) == LOCK_TUESDAY - Calendar.WEDNESDAY -> (lockedDays and LOCK_WEDNESDAY) == LOCK_WEDNESDAY - Calendar.THURSDAY -> (lockedDays and LOCK_THURSDAY) == LOCK_THURSDAY - Calendar.FRIDAY -> (lockedDays and LOCK_FRIDAY) == LOCK_FRIDAY - Calendar.SATURDAY -> (lockedDays and LOCK_SATURDAY) == LOCK_SATURDAY - else -> false - } - - if (!lockedToday) { - return false - } - // SY <-- - - return lockState == LockState.ACTIVE - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt index 5853fec9d..3b4669f11 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesPresenter.kt @@ -20,7 +20,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.flowOf diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt index 379c776ed..1de415818 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt @@ -76,11 +76,6 @@ object AuthenticatorUtil { activity?.toast(errString.toString()) cont.resume(false) } - - override fun onAuthenticationFailed(activity: FragmentActivity?) { - super.onAuthenticationFailed(activity) - cont.resume(false) - } }, ) } @@ -136,15 +131,5 @@ object AuthenticatorUtil { ) { isAuthenticating = false } - - /** - * Called when an authentication attempt by the user has been rejected. - * - * @param activity The activity that is currently hosting the prompt. - */ - @CallSuper - override fun onAuthenticationFailed(activity: FragmentActivity?) { - isAuthenticating = false - } } }