From 27ecd1d5e316ebd70e83e85597d251e195f572e8 Mon Sep 17 00:00:00 2001 From: Tanuj Date: Fri, 28 Feb 2025 17:20:39 +0530 Subject: Improved screen time calculations --- app/src/main/java/app/olauncher/MainViewModel.kt | 115 ++++++++++++++++++--- app/src/main/java/app/olauncher/data/Prefs.kt | 5 + .../java/app/olauncher/helper/AppUsageStats.kt | 21 ++++ 3 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/app/olauncher/helper/AppUsageStats.kt (limited to 'app/src/main') diff --git a/app/src/main/java/app/olauncher/MainViewModel.kt b/app/src/main/java/app/olauncher/MainViewModel.kt index 735b527..96078a0 100644 --- a/app/src/main/java/app/olauncher/MainViewModel.kt +++ b/app/src/main/java/app/olauncher/MainViewModel.kt @@ -2,12 +2,12 @@ package app.olauncher import android.app.Application import android.app.Service.USAGE_STATS_SERVICE +import android.app.usage.UsageEvents import android.app.usage.UsageStatsManager import android.content.ComponentName import android.content.Context import android.content.pm.LauncherApps import android.os.UserHandle -import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -19,17 +19,20 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import app.olauncher.data.AppModel import app.olauncher.data.Constants -import app.olauncher.data.Constants.ONE_DAY_IN_MILLIS import app.olauncher.data.Prefs +import app.olauncher.helper.AppUsageStats +import app.olauncher.helper.AppUsageStatsBucket import app.olauncher.helper.SingleLiveEvent import app.olauncher.helper.WallpaperWorker +import app.olauncher.helper.convertEpochToMidnight import app.olauncher.helper.formattedTimeSpent import app.olauncher.helper.getAppsList +import app.olauncher.helper.hasBeenMinutes import app.olauncher.helper.isOlauncherDefault import app.olauncher.helper.showToast import kotlinx.coroutines.launch -import java.util.Calendar import java.util.concurrent.TimeUnit +import kotlin.math.max class MainViewModel(application: Application) : AndroidViewModel(application) { @@ -253,22 +256,102 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } fun getTodaysScreenTime() { - viewModelScope.launch { - val usageStatsManager = appContext.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager + if (prefs.screenTimeLastUpdated.hasBeenMinutes(1).not()) { + appContext.showToast("return") + return + } + appContext.showToast("screen time") + val usageStatsManager = appContext.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager + val appUsageStatsHashMap: MutableMap = HashMap() + val beginTime = System.currentTimeMillis().convertEpochToMidnight() + val endTime = System.currentTimeMillis() + val events = usageStatsManager.queryEvents(beginTime, endTime) + val eventsMap: MutableMap> = HashMap() + var currentEvent: UsageEvents.Event + + while (events.hasNextEvent()) { + currentEvent = UsageEvents.Event() + if (events.getNextEvent(currentEvent)) { + when (currentEvent.eventType) { + UsageEvents.Event.ACTIVITY_RESUMED, UsageEvents.Event.ACTIVITY_PAUSED, UsageEvents.Event.ACTIVITY_STOPPED, UsageEvents.Event.FOREGROUND_SERVICE_START, UsageEvents.Event.FOREGROUND_SERVICE_STOP -> { + var packageEvents = eventsMap[currentEvent.packageName] + if (packageEvents == null) + packageEvents = ArrayList(listOf(currentEvent)) + else + packageEvents.add(currentEvent) + eventsMap[currentEvent.packageName] = packageEvents + } + } + } + } - val calendar = Calendar.getInstance() - calendar.set(Calendar.HOUR_OF_DAY, 0) - calendar.set(Calendar.MINUTE, 0) - calendar.set(Calendar.SECOND, 0) + for ((key, value) in eventsMap) { + val foregroundBucket = AppUsageStatsBucket() + val backgroundBucketMap: MutableMap = HashMap() + var pos = 0 + while (pos < value.size) { + val event = value[pos] + if (event.className != null) { + var backgroundBucket: AppUsageStatsBucket? = backgroundBucketMap[event.className] + if (backgroundBucket == null) { + backgroundBucket = AppUsageStatsBucket() + backgroundBucketMap[event.className] = backgroundBucket + } + when (event.eventType) { + UsageEvents.Event.ACTIVITY_RESUMED -> foregroundBucket.startMillis = event.timeStamp + + UsageEvents.Event.ACTIVITY_PAUSED, UsageEvents.Event.ACTIVITY_STOPPED -> if (foregroundBucket.startMillis >= foregroundBucket.endMillis) { + if (foregroundBucket.startMillis == 0L) { + foregroundBucket.startMillis = beginTime + } + foregroundBucket.endMillis = event.timeStamp + foregroundBucket.addTotalTime() + } + + UsageEvents.Event.FOREGROUND_SERVICE_START -> backgroundBucket.startMillis = event.timeStamp + UsageEvents.Event.FOREGROUND_SERVICE_STOP -> if (backgroundBucket.startMillis >= backgroundBucket.endMillis) { + if (backgroundBucket.startMillis == 0L) { + backgroundBucket.startMillis = beginTime + } + backgroundBucket.endMillis = event.timeStamp + backgroundBucket.addTotalTime() + } + } + if (pos == value.size - 1) { + if (foregroundBucket.startMillis > foregroundBucket.endMillis) { + foregroundBucket.endMillis = endTime + foregroundBucket.addTotalTime() + } + if (backgroundBucket.startMillis > backgroundBucket.endMillis) { + backgroundBucket.endMillis = endTime + backgroundBucket.addTotalTime() + } + } + } + pos++ + } - val usageStats = usageStatsManager.queryUsageStats( - UsageStatsManager.INTERVAL_DAILY, - calendar.timeInMillis, - calendar.timeInMillis + ONE_DAY_IN_MILLIS + val foregroundEnd: Long = foregroundBucket.endMillis + val totalTimeForeground: Long = foregroundBucket.totalTime + val backgroundEnd: Long = backgroundBucketMap.values + .mapNotNull { it?.endMillis } + .maxOrNull() ?: 0L + + val totalTimeBackground: Long = backgroundBucketMap.values + .mapNotNull { it?.totalTime } + .sum() + + appUsageStatsHashMap[key] = AppUsageStats( + max(foregroundEnd, backgroundEnd), + totalTimeForeground, + backgroundEnd, + totalTimeBackground ) - val totalTimeInMillis = usageStats.sumOf { it.totalTimeInForeground } - val viewTimeSpent = appContext.formattedTimeSpent(totalTimeInMillis) - screenTimeValue.postValue(viewTimeSpent) } + + val totalTimeInMillis = appUsageStatsHashMap.values.sumOf { it.totalTimeInForegroundMillis } + val viewTimeSpent = appContext.formattedTimeSpent(totalTimeInMillis) + screenTimeValue.postValue(viewTimeSpent) + prefs.screenTimeLastUpdated = endTime } } \ No newline at end of file diff --git a/app/src/main/java/app/olauncher/data/Prefs.kt b/app/src/main/java/app/olauncher/data/Prefs.kt index 62f164d..758fc74 100644 --- a/app/src/main/java/app/olauncher/data/Prefs.kt +++ b/app/src/main/java/app/olauncher/data/Prefs.kt @@ -39,6 +39,7 @@ class Prefs(context: Context) { private val TEXT_SIZE_SCALE = "TEXT_SIZE_SCALE" private val PRO_MESSAGE_SHOWN = "PRO_MESSAGE_SHOWN" private val HIDE_SET_DEFAULT_LAUNCHER = "HIDE_SET_DEFAULT_LAUNCHER" + private val SCREEN_TIME_LAST_UPDATED = "SCREEN_TIME_LAST_UPDATED" private val APP_NAME_1 = "APP_NAME_1" private val APP_NAME_2 = "APP_NAME_2" @@ -178,6 +179,10 @@ class Prefs(context: Context) { get() = prefs.getBoolean(HIDE_SET_DEFAULT_LAUNCHER, false) set(value) = prefs.edit().putBoolean(HIDE_SET_DEFAULT_LAUNCHER, value).apply() + var screenTimeLastUpdated: Long + get() = prefs.getLong(SCREEN_TIME_LAST_UPDATED, 0L) + set(value) = prefs.edit().putLong(SCREEN_TIME_LAST_UPDATED, value).apply() + var hiddenApps: MutableSet get() = prefs.getStringSet(HIDDEN_APPS, mutableSetOf()) as MutableSet set(value) = prefs.edit().putStringSet(HIDDEN_APPS, value).apply() diff --git a/app/src/main/java/app/olauncher/helper/AppUsageStats.kt b/app/src/main/java/app/olauncher/helper/AppUsageStats.kt new file mode 100644 index 0000000..9b69269 --- /dev/null +++ b/app/src/main/java/app/olauncher/helper/AppUsageStats.kt @@ -0,0 +1,21 @@ +package app.olauncher.helper + +import android.os.Build +import androidx.annotation.RequiresApi + +class AppUsageStats( + val lastTimeUsedMillis: Long, + val totalTimeInForegroundMillis: Long, + @get:RequiresApi(Build.VERSION_CODES.Q) val lastTimeForegroundServiceUsedMillis: Long, + @get:RequiresApi(Build.VERSION_CODES.Q) val totalTimeForegroundServiceUsedMillis: Long, +) + +class AppUsageStatsBucket { + var startMillis: Long = 0L + var endMillis: Long = 0L + var totalTime: Long = 0L + + fun addTotalTime() { + this.totalTime += endMillis - startMillis + } +} \ No newline at end of file -- cgit v1.2.1