Only update session duration start times after five seconds usage

This commit is contained in:
Jonas Lochmann 2021-08-16 02:00:00 +02:00
parent a8bb991501
commit adc333fd40
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
3 changed files with 136 additions and 27 deletions

View file

@ -128,6 +128,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
timeApi = appLogic.timeApi,
longDuration = 1000 * 60 * 10 /* 10 minutes */
)
private val undisturbedCategoryUsageCounter = UndisturbedCategoryUsageCounter()
private val appTitleCache = QueryAppTitleCache(appLogic.platformIntegration)
private val categoryHandlingCache = CategoryHandlingCache()
@ -194,6 +195,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
// app must be enabled
if (!appLogic.enable.waitForNonNullValue()) {
commitUsedTimeUpdaters()
undisturbedCategoryUsageCounter.reset()
appLogic.platformIntegration.setAppStatusMessage(null)
appLogic.platformIntegration.setShowBlockingOverlay(false)
setShowNotificationToRevokeTemporarilyAllowedApps(false)
@ -215,6 +217,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
// device must be used by a child
if (deviceRelatedData == null || userRelatedData == null || userRelatedData.user.type != UserType.Child) {
commitUsedTimeUpdaters()
undisturbedCategoryUsageCounter.reset()
val shouldDoAutomaticSignOut = deviceRelatedData != null && DefaultUserLogic.hasAutomaticSignOut(deviceRelatedData) && deviceRelatedData.canSwitchToDefaultUser
@ -248,6 +251,7 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
val nowTimestamp = realTime.timeInMillis
val nowTimezone = TimeZone.getTimeZone(userRelatedData.user.timeZone)
val nowUptime = appLogic.timeApi.getCurrentUptimeInMillis()
val nowDate = DateInTimezone.getLocalDate(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()
}.flattenToSet()
undisturbedCategoryUsageCounter.report(nowUptime, currentCategoryIds)
val recentlyStartedCategories = undisturbedCategoryUsageCounter.getRecentlyStartedCategories(nowUptime)
val needsNetworkId = allAppsBaseHandlings.find { it.needsNetworkId() } != null
val networkId: NetworkId? = if (needsNetworkId) appLogic.platformIntegration.getCurrentNetworkId() else null
@ -496,7 +503,8 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
duration = timeToSubtract,
dayOfEpoch = dayOfEpoch,
trustedTimestamp = if (realTime.shouldTrustTimePermanently) realTime.timeInMillis else 0,
handlings = categoryHandlingsToCount
handlings = categoryHandlingsToCount,
recentlyStartedCategories = recentlyStartedCategories
)
if (didAutoCommitOfUsedTimes) {

View file

@ -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
}
}
}
}

View file

@ -32,6 +32,7 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
private var countedTime = 0
private var lastCategoryHandlings = emptyList<CategoryItselfHandling>()
private var categoryIds = emptySet<String>()
private var recentlyStartedCategories = emptySet<String>()
private var trustedTimestamp = 0L
private var dayOfEpoch = 0
private var maxTimeToAdd = Long.MAX_VALUE
@ -40,7 +41,13 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
fun getCountedCategoryIds() = categoryIds
// 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) {
throw IllegalArgumentException()
}
@ -94,6 +101,7 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
}
this.lastCategoryHandlings = handlings
this.recentlyStartedCategories = recentlyStartedCategories
this.trustedTimestamp = trustedTimestamp
this.dayOfEpoch = dayOfEpoch
@ -111,40 +119,82 @@ class UsedTimeUpdateHelper (private val appLogic: AppLogic) {
val makeCommit = lastCategoryHandlings.isNotEmpty() && countedTime > 0
if (makeCommit) {
try {
ApplyActionUtil.applyAppLogicAction(
action = AddUsedTimeActionVersion2(
dayOfEpoch = dayOfEpoch,
items = lastCategoryHandlings.map { handling ->
AddUsedTimeActionItem(
categoryId = handling.createdWithCategoryRelatedData.category.id,
timeToAdd = countedTime,
extraTimeToSubtract = if (handling.shouldCountExtraTime) countedTime else 0,
additionalCountingSlots = handling.additionalTimeCountingSlots,
sessionDurationLimits = handling.sessionDurationSlotsToCount
)
},
trustedTimestamp = if (lastCategoryHandlings.find { it.sessionDurationSlotsToCount.isNotEmpty() } != null) trustedTimestamp else 0
),
appLogic = appLogic,
ignoreIfDeviceIsNotConfigured = true
val categoriesWithSessionDurationLimits = lastCategoryHandlings
.filter { it.sessionDurationSlotsToCount.isNotEmpty() }
.map { it.createdWithCategoryRelatedData.category.id }
.toSet()
val categoriesWithSessionDurationLimitsWhichWereRecentlyStarted = categoriesWithSessionDurationLimits.intersect(recentlyStartedCategories)
if (categoriesWithSessionDurationLimitsWhichWereRecentlyStarted.isEmpty()) {
commitSendAction(
items = lastCategoryHandlings,
sendTimestamp = categoriesWithSessionDurationLimits.isNotEmpty()
)
} catch (ex: CategoryNotFoundException) {
} else {
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;
// in this case there could be some lost time
// changes for other categories, but it's no big problem
if (categoriesWithSessionDurationLimits == categoriesWithSessionDurationLimitsWhichWereRecentlyStarted) {
// no category needs the timestamp
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
// doing this would cause a commit very soon again
// lastCategoryHandlings = emptyList()
// categoryIds = emptySet()
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
)
}
}