mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-04 02:09:19 +02:00
Only update session duration start times after five seconds usage
This commit is contained in:
parent
a8bb991501
commit
adc333fd40
3 changed files with 136 additions and 27 deletions
|
@ -128,6 +128,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
timeApi = appLogic.timeApi,
|
timeApi = appLogic.timeApi,
|
||||||
longDuration = 1000 * 60 * 10 /* 10 minutes */
|
longDuration = 1000 * 60 * 10 /* 10 minutes */
|
||||||
)
|
)
|
||||||
|
private val undisturbedCategoryUsageCounter = UndisturbedCategoryUsageCounter()
|
||||||
|
|
||||||
private val appTitleCache = QueryAppTitleCache(appLogic.platformIntegration)
|
private val appTitleCache = QueryAppTitleCache(appLogic.platformIntegration)
|
||||||
private val categoryHandlingCache = CategoryHandlingCache()
|
private val categoryHandlingCache = CategoryHandlingCache()
|
||||||
|
@ -194,6 +195,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
// app must be enabled
|
// app must be enabled
|
||||||
if (!appLogic.enable.waitForNonNullValue()) {
|
if (!appLogic.enable.waitForNonNullValue()) {
|
||||||
commitUsedTimeUpdaters()
|
commitUsedTimeUpdaters()
|
||||||
|
undisturbedCategoryUsageCounter.reset()
|
||||||
appLogic.platformIntegration.setAppStatusMessage(null)
|
appLogic.platformIntegration.setAppStatusMessage(null)
|
||||||
appLogic.platformIntegration.setShowBlockingOverlay(false)
|
appLogic.platformIntegration.setShowBlockingOverlay(false)
|
||||||
setShowNotificationToRevokeTemporarilyAllowedApps(false)
|
setShowNotificationToRevokeTemporarilyAllowedApps(false)
|
||||||
|
@ -215,6 +217,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
// device must be used by a child
|
// device must be used by a child
|
||||||
if (deviceRelatedData == null || userRelatedData == null || userRelatedData.user.type != UserType.Child) {
|
if (deviceRelatedData == null || userRelatedData == null || userRelatedData.user.type != UserType.Child) {
|
||||||
commitUsedTimeUpdaters()
|
commitUsedTimeUpdaters()
|
||||||
|
undisturbedCategoryUsageCounter.reset()
|
||||||
|
|
||||||
val shouldDoAutomaticSignOut = deviceRelatedData != null && DefaultUserLogic.hasAutomaticSignOut(deviceRelatedData) && deviceRelatedData.canSwitchToDefaultUser
|
val shouldDoAutomaticSignOut = deviceRelatedData != null && DefaultUserLogic.hasAutomaticSignOut(deviceRelatedData) && deviceRelatedData.canSwitchToDefaultUser
|
||||||
|
|
||||||
|
@ -248,6 +251,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
|
|
||||||
val nowTimestamp = realTime.timeInMillis
|
val nowTimestamp = realTime.timeInMillis
|
||||||
val nowTimezone = TimeZone.getTimeZone(userRelatedData.user.timeZone)
|
val nowTimezone = TimeZone.getTimeZone(userRelatedData.user.timeZone)
|
||||||
|
val nowUptime = appLogic.timeApi.getCurrentUptimeInMillis()
|
||||||
|
|
||||||
val nowDate = DateInTimezone.getLocalDate(nowTimestamp, nowTimezone)
|
val nowDate = DateInTimezone.getLocalDate(nowTimestamp, nowTimezone)
|
||||||
val nowMinuteOfWeek = getMinuteOfWeek(nowTimestamp, nowTimezone)
|
val nowMinuteOfWeek = getMinuteOfWeek(nowTimestamp, nowTimezone)
|
||||||
|
@ -323,6 +327,9 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
if (it is AppBaseHandling.UseCategories) it.categoryIds else emptySet()
|
if (it is AppBaseHandling.UseCategories) it.categoryIds else emptySet()
|
||||||
}.flattenToSet()
|
}.flattenToSet()
|
||||||
|
|
||||||
|
undisturbedCategoryUsageCounter.report(nowUptime, currentCategoryIds)
|
||||||
|
val recentlyStartedCategories = undisturbedCategoryUsageCounter.getRecentlyStartedCategories(nowUptime)
|
||||||
|
|
||||||
val needsNetworkId = allAppsBaseHandlings.find { it.needsNetworkId() } != null
|
val needsNetworkId = allAppsBaseHandlings.find { it.needsNetworkId() } != null
|
||||||
val networkId: NetworkId? = if (needsNetworkId) appLogic.platformIntegration.getCurrentNetworkId() else null
|
val networkId: NetworkId? = if (needsNetworkId) appLogic.platformIntegration.getCurrentNetworkId() else null
|
||||||
|
|
||||||
|
@ -496,7 +503,8 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
duration = timeToSubtract,
|
duration = timeToSubtract,
|
||||||
dayOfEpoch = dayOfEpoch,
|
dayOfEpoch = dayOfEpoch,
|
||||||
trustedTimestamp = if (realTime.shouldTrustTimePermanently) realTime.timeInMillis else 0,
|
trustedTimestamp = if (realTime.shouldTrustTimePermanently) realTime.timeInMillis else 0,
|
||||||
handlings = categoryHandlingsToCount
|
handlings = categoryHandlingsToCount,
|
||||||
|
recentlyStartedCategories = recentlyStartedCategories
|
||||||
)
|
)
|
||||||
|
|
||||||
if (didAutoCommitOfUsedTimes) {
|
if (didAutoCommitOfUsedTimes) {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2021 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
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.logic
|
||||||
|
|
||||||
|
class UndisturbedCategoryUsageCounter {
|
||||||
|
private val data = mutableMapOf<String, Long>()
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
data.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun report(uptime: Long, categories: Set<String>) {
|
||||||
|
removeUnknown(categories)
|
||||||
|
addNewCategories(categories, uptime)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRecentlyStartedCategories(uptime: Long): Set<String> {
|
||||||
|
return data.filterValues { it >= uptime - 5000 }.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeUnknown(currentCategories: Set<String>) {
|
||||||
|
val iterator = data.iterator()
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
if (!currentCategories.contains(iterator.next().key)) {
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addNewCategories(categories: Set<String>, uptime: Long) {
|
||||||
|
categories.forEach { categoryId ->
|
||||||
|
if (!data.containsKey(categoryId)) {
|
||||||
|
data[categoryId] = uptime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
|
||||||
private var countedTime = 0
|
private var countedTime = 0
|
||||||
private var lastCategoryHandlings = emptyList<CategoryItselfHandling>()
|
private var lastCategoryHandlings = emptyList<CategoryItselfHandling>()
|
||||||
private var categoryIds = emptySet<String>()
|
private var categoryIds = emptySet<String>()
|
||||||
|
private var recentlyStartedCategories = emptySet<String>()
|
||||||
private var trustedTimestamp = 0L
|
private var trustedTimestamp = 0L
|
||||||
private var dayOfEpoch = 0
|
private var dayOfEpoch = 0
|
||||||
private var maxTimeToAdd = Long.MAX_VALUE
|
private var maxTimeToAdd = Long.MAX_VALUE
|
||||||
|
@ -40,7 +41,13 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
|
||||||
fun getCountedCategoryIds() = categoryIds
|
fun getCountedCategoryIds() = categoryIds
|
||||||
|
|
||||||
// returns true if it made a commit
|
// returns true if it made a commit
|
||||||
suspend fun report(duration: Int, handlings: List<CategoryItselfHandling>, trustedTimestamp: Long, dayOfEpoch: Int): Boolean {
|
suspend fun report(
|
||||||
|
duration: Int,
|
||||||
|
handlings: List<CategoryItselfHandling>,
|
||||||
|
recentlyStartedCategories: Set<String>,
|
||||||
|
trustedTimestamp: Long,
|
||||||
|
dayOfEpoch: Int
|
||||||
|
): Boolean {
|
||||||
if (handlings.find { !it.shouldCountTime } != null || duration < 0) {
|
if (handlings.find { !it.shouldCountTime } != null || duration < 0) {
|
||||||
throw IllegalArgumentException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
@ -94,6 +101,7 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastCategoryHandlings = handlings
|
this.lastCategoryHandlings = handlings
|
||||||
|
this.recentlyStartedCategories = recentlyStartedCategories
|
||||||
this.trustedTimestamp = trustedTimestamp
|
this.trustedTimestamp = trustedTimestamp
|
||||||
this.dayOfEpoch = dayOfEpoch
|
this.dayOfEpoch = dayOfEpoch
|
||||||
|
|
||||||
|
@ -111,40 +119,82 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
|
||||||
val makeCommit = lastCategoryHandlings.isNotEmpty() && countedTime > 0
|
val makeCommit = lastCategoryHandlings.isNotEmpty() && countedTime > 0
|
||||||
|
|
||||||
if (makeCommit) {
|
if (makeCommit) {
|
||||||
try {
|
val categoriesWithSessionDurationLimits = lastCategoryHandlings
|
||||||
ApplyActionUtil.applyAppLogicAction(
|
.filter { it.sessionDurationSlotsToCount.isNotEmpty() }
|
||||||
action = AddUsedTimeActionVersion2(
|
.map { it.createdWithCategoryRelatedData.category.id }
|
||||||
dayOfEpoch = dayOfEpoch,
|
.toSet()
|
||||||
items = lastCategoryHandlings.map { handling ->
|
|
||||||
AddUsedTimeActionItem(
|
val categoriesWithSessionDurationLimitsWhichWereRecentlyStarted = categoriesWithSessionDurationLimits.intersect(recentlyStartedCategories)
|
||||||
categoryId = handling.createdWithCategoryRelatedData.category.id,
|
|
||||||
timeToAdd = countedTime,
|
if (categoriesWithSessionDurationLimitsWhichWereRecentlyStarted.isEmpty()) {
|
||||||
extraTimeToSubtract = if (handling.shouldCountExtraTime) countedTime else 0,
|
commitSendAction(
|
||||||
additionalCountingSlots = handling.additionalTimeCountingSlots,
|
items = lastCategoryHandlings,
|
||||||
sessionDurationLimits = handling.sessionDurationSlotsToCount
|
sendTimestamp = categoriesWithSessionDurationLimits.isNotEmpty()
|
||||||
)
|
|
||||||
},
|
|
||||||
trustedTimestamp = if (lastCategoryHandlings.find { it.sessionDurationSlotsToCount.isNotEmpty() } != null) trustedTimestamp else 0
|
|
||||||
),
|
|
||||||
appLogic = appLogic,
|
|
||||||
ignoreIfDeviceIsNotConfigured = true
|
|
||||||
)
|
)
|
||||||
} catch (ex: CategoryNotFoundException) {
|
} else {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(LOG_TAG, "could not commit used times", ex)
|
Log.d(LOG_TAG, "skip updating the session duration last usage timestamps")
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a very rare case if a category is deleted while it is used;
|
if (categoriesWithSessionDurationLimits == categoriesWithSessionDurationLimitsWhichWereRecentlyStarted) {
|
||||||
// in this case there could be some lost time
|
// no category needs the timestamp
|
||||||
// changes for other categories, but it's no big problem
|
commitSendAction(
|
||||||
|
items = lastCategoryHandlings,
|
||||||
|
sendTimestamp = false
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(LOG_TAG, "... but only for some categories")
|
||||||
|
}
|
||||||
|
|
||||||
|
// some categories need the timestamp and others do not
|
||||||
|
val items1 = lastCategoryHandlings.filter {
|
||||||
|
categoriesWithSessionDurationLimitsWhichWereRecentlyStarted.contains(it.createdWithCategoryRelatedData.category.id)
|
||||||
|
}
|
||||||
|
val items2 = lastCategoryHandlings.filterNot {
|
||||||
|
categoriesWithSessionDurationLimitsWhichWereRecentlyStarted.contains(it.createdWithCategoryRelatedData.category.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
commitSendAction(items = items1, sendTimestamp = false)
|
||||||
|
commitSendAction(items = items2, sendTimestamp = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
countedTime = 0
|
countedTime = 0
|
||||||
// doing this would cause a commit very soon again
|
|
||||||
// lastCategoryHandlings = emptyList()
|
|
||||||
// categoryIds = emptySet()
|
|
||||||
|
|
||||||
return makeCommit
|
return makeCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun commitSendAction(items: List<CategoryItselfHandling>, sendTimestamp: Boolean) {
|
||||||
|
try {
|
||||||
|
ApplyActionUtil.applyAppLogicAction(
|
||||||
|
action = AddUsedTimeActionVersion2(
|
||||||
|
dayOfEpoch = dayOfEpoch,
|
||||||
|
items = items.map { handling -> prepareHandling(handling) },
|
||||||
|
trustedTimestamp = if (sendTimestamp) trustedTimestamp else 0
|
||||||
|
),
|
||||||
|
appLogic = appLogic,
|
||||||
|
ignoreIfDeviceIsNotConfigured = true
|
||||||
|
)
|
||||||
|
} catch (ex: CategoryNotFoundException) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(LOG_TAG, "could not commit used times", ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a very rare case if a category is deleted while it is used;
|
||||||
|
// in this case there could be some lost time
|
||||||
|
// changes for other categories, but it's no big problem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareHandling(handling: CategoryItselfHandling): AddUsedTimeActionItem {
|
||||||
|
return AddUsedTimeActionItem(
|
||||||
|
categoryId = handling.createdWithCategoryRelatedData.category.id,
|
||||||
|
timeToAdd = countedTime,
|
||||||
|
extraTimeToSubtract = if (handling.shouldCountExtraTime) countedTime else 0,
|
||||||
|
additionalCountingSlots = handling.additionalTimeCountingSlots,
|
||||||
|
sessionDurationLimits = handling.sessionDurationSlotsToCount
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue