Process network ids for the limit login categories

This commit is contained in:
Jonas Lochmann 2022-06-27 02:00:00 +02:00
parent 2f85752b97
commit c1f79878fc
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,25 +23,51 @@ import io.timelimit.android.data.extensions.getCategoryWithParentCategories
import io.timelimit.android.data.model.ExperimentalFlags
import io.timelimit.android.data.model.derived.CompleteUserLoginRelatedData
import io.timelimit.android.integration.platform.BatteryStatus
import io.timelimit.android.integration.platform.NetworkId
import io.timelimit.android.livedata.ignoreUnchanged
import io.timelimit.android.livedata.liveDataFromFunction
import io.timelimit.android.logic.AppLogic
import io.timelimit.android.logic.BlockingReason
import io.timelimit.android.logic.RealTime
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
import java.lang.IllegalStateException
import java.util.concurrent.CountDownLatch
sealed class AllowUserLoginStatus {
data class Allow(val maxTime: Long): AllowUserLoginStatus()
data class ForbidByCategory(val categoryTitle: String, val blockingReason: BlockingReason, val maxTime: Long): AllowUserLoginStatus()
open val dependsOnNetworkId = false
data class Allow(
val maxTime: Long,
override val dependsOnNetworkId: Boolean
): AllowUserLoginStatus()
data class ForbidByCategory(
val categoryTitle: String,
val blockingReason: BlockingReason,
val maxTime: Long,
override val dependsOnNetworkId: Boolean
): AllowUserLoginStatus()
object ForbidByMissingSync: AllowUserLoginStatus()
object ForbidUserNotFound: AllowUserLoginStatus()
}
object AllowUserLoginStatusUtil {
private fun calculate(data: CompleteUserLoginRelatedData, time: RealTime, cache: CategoryHandlingCache, batteryStatus: BatteryStatus, missingSyncForLimitLoginUser: Boolean): AllowUserLoginStatus = synchronized(cache) {
private fun calculate(
data: CompleteUserLoginRelatedData,
time: RealTime,
cache: CategoryHandlingCache,
batteryStatus: BatteryStatus,
missingSyncForLimitLoginUser: Boolean,
currentNetworkId: NetworkId?
): AllowUserLoginStatus = synchronized(cache) {
val hasPremium = data.deviceRelatedData.isConnectedAndHasPremium || data.deviceRelatedData.isLocalMode
if (!hasPremium) {
return AllowUserLoginStatus.Allow(maxTime = Long.MAX_VALUE)
return AllowUserLoginStatus.Allow(
maxTime = Long.MAX_VALUE,
dependsOnNetworkId = false
)
}
return if (data.limitLoginCategoryUserRelatedData != null && data.loginRelatedData.limitLoginCategory != null) {
@ -52,6 +78,7 @@ object AllowUserLoginStatusUtil {
val preBlockDuration = data.loginRelatedData.limitLoginCategory.preBlockDuration
val maxCheckedTime = time.timeInMillis + preBlockDuration
val categoryIds = data.limitLoginCategoryUserRelatedData.getCategoryWithParentCategories(data.loginRelatedData.limitLoginCategory.categoryId)
var dependsOnAnyNetworkId = false
while (true) {
cache.reportStatus(
@ -60,18 +87,21 @@ object AllowUserLoginStatusUtil {
timeInMillis = currentCheckedTime,
shouldTrustTimeTemporarily = time.shouldTrustTimeTemporarily,
batteryStatus = batteryStatus,
currentNetworkId = null, // only checks shouldBlockAtSystemLevel which ignores the network id
currentNetworkId = currentNetworkId,
hasPremiumOrLocalMode = data.deviceRelatedData.isLocalMode || data.deviceRelatedData.isConnectedAndHasPremium
)
val handlings = categoryIds.map { cache.get(it) }
val remainingTimeToCheck = maxCheckedTime - currentCheckedTime
dependsOnAnyNetworkId = dependsOnAnyNetworkId or (handlings.find { it.dependsOnNetworkId } != null)
handlings.find { it.remainingSessionDuration != null && it.remainingSessionDuration < remainingTimeToCheck }?.let { blockingHandling ->
return AllowUserLoginStatus.ForbidByCategory(
categoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title,
blockingReason = BlockingReason.SessionDurationLimit,
maxTime = blockingHandling.dependsOnMaxTime
maxTime = blockingHandling.dependsOnMaxTime,
dependsOnNetworkId = dependsOnAnyNetworkId
)
}
@ -79,7 +109,8 @@ object AllowUserLoginStatusUtil {
return AllowUserLoginStatus.ForbidByCategory(
categoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title,
blockingReason = BlockingReason.TimeOver,
maxTime = blockingHandling.dependsOnMaxTime
maxTime = blockingHandling.dependsOnMaxTime,
dependsOnNetworkId = dependsOnAnyNetworkId
)
}
@ -87,7 +118,19 @@ object AllowUserLoginStatusUtil {
return AllowUserLoginStatus.ForbidByCategory(
categoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title,
blockingReason = blockingHandling.systemLevelBlockingReason,
maxTime = blockingHandling.dependsOnMaxTime
maxTime = blockingHandling.dependsOnMaxTime,
dependsOnNetworkId = dependsOnAnyNetworkId
)
}
handlings.find { !it.okByNetworkId }?.let { blockingHandling ->
if (!dependsOnAnyNetworkId) throw IllegalStateException()
return AllowUserLoginStatus.ForbidByCategory(
categoryTitle = blockingHandling.createdWithCategoryRelatedData.category.title,
blockingReason = blockingHandling.activityBlockingReason,
maxTime = Long.MAX_VALUE,
dependsOnNetworkId = dependsOnAnyNetworkId
)
}
@ -101,12 +144,14 @@ object AllowUserLoginStatusUtil {
val maxTimeByCategories = categoryIds.map { cache.get(it) }.minByOrNull { it.dependsOnMaxTime }?.dependsOnMaxTime ?: Long.MAX_VALUE
AllowUserLoginStatus.Allow(
maxTime = (maxTimeByCategories - preBlockDuration).coerceAtLeast(time.timeInMillis + 1000)
maxTime = (maxTimeByCategories - preBlockDuration).coerceAtLeast(time.timeInMillis + 1000),
dependsOnNetworkId = dependsOnAnyNetworkId
)
}
} else {
AllowUserLoginStatus.Allow(
maxTime = Long.MAX_VALUE
maxTime = Long.MAX_VALUE,
dependsOnNetworkId = false
)
}
}
@ -118,27 +163,54 @@ object AllowUserLoginStatusUtil {
val userRelatedData = logic.database.derivedDataDao().getUserLoginRelatedDataSync(userId) ?: return AllowUserLoginStatus.ForbidUserNotFound
val realTime = RealTime.newInstance()
val batteryStatus = logic.platformIntegration.getBatteryStatus()
val latch = CountDownLatch(1)
Threads.mainThreadHandler.post {
logic.realTimeLogic.getRealTime(realTime)
latch.countDown()
CountDownLatch(1).let { latch ->
Threads.mainThreadHandler.post {
logic.realTimeLogic.getRealTime(realTime)
latch.countDown()
}
latch.await()
}
latch.await()
val attempt1 = calculate(
data = userRelatedData,
batteryStatus = batteryStatus,
time = realTime,
cache = CategoryHandlingCache(),
missingSyncForLimitLoginUser = requireSyncOnLimitLoginUser && !didSync,
currentNetworkId = null
)
return calculate(
return if (attempt1.dependsOnNetworkId) {
val currentNetworkId = CountDownLatch(1).let { latch ->
var currentNetworkId: NetworkId? = null
Threads.mainThreadHandler.post {
currentNetworkId = logic.platformIntegration.getCurrentNetworkId()
latch.countDown()
}
latch.await()
currentNetworkId!!
}
calculate(
data = userRelatedData,
batteryStatus = batteryStatus,
time = realTime,
cache = CategoryHandlingCache(),
missingSyncForLimitLoginUser = requireSyncOnLimitLoginUser && !didSync
)
missingSyncForLimitLoginUser = requireSyncOnLimitLoginUser && !didSync,
currentNetworkId = currentNetworkId
)
} else attempt1
}
fun calculateLive(logic: AppLogic, userId: String, didSyncLive: LiveData<Boolean>): LiveData<AllowUserLoginStatus> = object : MediatorLiveData<AllowUserLoginStatus>() {
val cache = CategoryHandlingCache()
val time = RealTime.newInstance()
val currentNetworkIdLive = liveDataFromFunction { logic.platformIntegration.getCurrentNetworkId() }.ignoreUnchanged()
var batteryStatus: BatteryStatus? = null
var hasUserLoginRelatedData = false
var userLoginRelatedData: CompleteUserLoginRelatedData? = null
@ -147,6 +219,8 @@ object AllowUserLoginStatusUtil {
var isConnectedMode = false
var didLoadIsConnectedMode = false
var didSync = false
var isObservingNetworkId = false
var currentNetworkId: NetworkId? = null
init {
addSource(logic.platformIntegration.getBatteryStatusLive(), androidx.lifecycle.Observer {
@ -194,7 +268,8 @@ object AllowUserLoginStatusUtil {
batteryStatus = batteryStatus,
cache = cache,
time = time,
missingSyncForLimitLoginUser = isConnectedMode && requireSyncForLimitLoginUsers && !didSync
missingSyncForLimitLoginUser = isConnectedMode && requireSyncForLimitLoginUsers && !didSync,
currentNetworkId = currentNetworkId
)
if (result != value) {
@ -212,6 +287,20 @@ object AllowUserLoginStatusUtil {
logic.timeApi.cancelScheduledAction(updateRunnable)
logic.timeApi.runDelayedByUptime(updateRunnable, scheduledTime - time.timeInMillis)
}
if (result.dependsOnNetworkId != isObservingNetworkId) {
// important detail: the addSource can call update immediately
isObservingNetworkId = result.dependsOnNetworkId
if (result.dependsOnNetworkId) {
addSource(currentNetworkIdLive) {
currentNetworkId = it; update()
}
} else {
removeSource(currentNetworkIdLive)
currentNetworkId = null
}
}
}
override fun onActive() {