Olauncher icon indicating copy to clipboard operation
Olauncher copied to clipboard

fix(home screen): screen time corrected by filtering only today's values

Open OndrejKulhavy opened this issue 10 months ago • 21 comments

#545 Fix

OndrejKulhavy avatar Feb 23 '25 18:02 OndrejKulhavy

This makes it worse not better

Old Way Your Way
Old Way Your Way

CreativeCodeCat avatar Feb 23 '25 20:02 CreativeCodeCat

Check it out now.

Launcher apps are being filtered out.

In my case, I have 46 minutes shown in the launcher and 41 minutes in Digital Wellbeing.

OndrejKulhavy avatar Feb 23 '25 21:02 OndrejKulhavy

I've already tried the combination of the changes you've made. So far no luck. I read somewhere that timezone plays a role and should be taken into consideration. Need to test that theory.

Olauncher Digital Wellbeing
Olauncher Digital Wellbeing

tanujnotes avatar Feb 24 '25 05:02 tanujnotes

You are right! It unfortunately doesn't work for me today either.

OndrejKulhavy avatar Feb 24 '25 07:02 OndrejKulhavy

An important thing I didn’t notice yesterday is that I accidentally counted time only for the launcher apps instead of filtering them out. 🤦‍♂️ That’s why you have 0 minutes. With that being fixed. It seems promising. Will test it and play with it further.

Olauncher Digital Wellbeing
Olauncher Digital Wellbeing

OndrejKulhavy avatar Feb 24 '25 07:02 OndrejKulhavy

I will take a look in a bit and see what mine comes up with on my phone. Testing it on android studio sucks.

CreativeCodeCat avatar Feb 24 '25 07:02 CreativeCodeCat

firstTimeStamp seems to be having issues and always coming back as 0 where as lastTimeStamp gives data maybe that is what needs using? i could be wrong

CreativeCodeCat avatar Feb 24 '25 08:02 CreativeCodeCat

Below is the code I am using, along with a blacklist.xml to blacklist apps I don't want displayed in the usage.

I had to blacklist com.opera.browser because it behaves strangely, always showing background hours and not just the time it's actively being used.

fun getTotalScreenTime(context: Context): Long {
    val packageManager = context.packageManager
    val blacklist = parseBlacklistXML(context)
    val currentPackageName = context.packageName // Get current app's package name

    // Get start of today in the correct timezone
    val startOfToday = Calendar.getInstance().apply {
        timeZone = TimeZone.getDefault()
        set(Calendar.HOUR_OF_DAY, 0)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)
    }.timeInMillis

    val currentTime = Calendar.getInstance().apply {
        timeZone = TimeZone.getDefault()
    }.timeInMillis
    val usageStatsManager = context.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager

    // Get launcher apps (home screen apps)
    val launcherApps = packageManager.queryIntentActivities(
        Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME), 0
    ).map { it.activityInfo.packageName }.toSet()

    // Query usage stats for today
    val usageStatsList = usageStatsManager.queryUsageStats(
        UsageStatsManager.INTERVAL_DAILY, startOfToday, currentTime
    ) ?: return 0L

    return usageStatsList
        .filter { usageStats ->
            val packageName = usageStats.packageName
            // Exclude launcher apps, blacklisted apps, and current app
            packageName != currentPackageName &&
                    !launcherApps.contains(packageName) &&
                    !blacklist.contains(packageName) &&
                    usageStats.totalTimeInForeground > 0 &&
                    // Ensure usage happened today
                    startOfToday <= usageStats.lastTimeUsed
        }
        .sumOf { it.totalTimeInForeground }
}

private fun parseBlacklistXML(context: Context): List<String> {
    val packageNames = mutableListOf<String>()

    // Obtain an XmlPullParser for the blacklist.xml file
    context.resources.getXml(R.xml.blacklist).use { parser ->
        while (parser.eventType != XmlPullParser.END_DOCUMENT) {
            if (parser.eventType == XmlPullParser.START_TAG && parser.name == "app") {
                val packageName = parser.getAttributeValue(null, "packageName")
                packageNames.add(packageName)
            }
            parser.next()
        }
    }

    return packageNames
}

fun formatMillisToHMS(millis: Long, showSeconds: Boolean): String {
    val hours = millis / (1000 * 60 * 60)
    val minutes = (millis % (1000 * 60 * 60)) / (1000 * 60)
    val seconds = (millis % (1000 * 60)) / 1000

    val formattedString = StringBuilder()
    if (hours > 0) {
        formattedString.append("$hours h ")
    }
    if (minutes > 0 || hours > 0) {
        formattedString.append("$minutes m ")
    }
    // Only append seconds if showSeconds is true
    if (showSeconds) {
        formattedString.append("$seconds s")
    }

    return formattedString.toString().trim()
}

CreativeCodeCat avatar Feb 24 '25 10:02 CreativeCodeCat

Another option is to just ignore all this and use launcher screen on/off time to calculate the total screen time. Won't even need the screen time permission. 😅

tanujnotes avatar Feb 24 '25 10:02 tanujnotes

@CreativeCodeCat ... Does it work with the Opera being blocked? And if, how much is the time off in comparison to the digital wellbeing?

Because so far I have only one minute difference. Screenshot_20250224_123544_Digital Wellbeing.jpg

Screenshot_20250224_123534_Olauncher.jpg

OndrejKulhavy avatar Feb 24 '25 11:02 OndrejKulhavy

I will try to replicate the issue opera...

OndrejKulhavy avatar Feb 24 '25 11:02 OndrejKulhavy

Another option is to just ignore all this and use launcher screen on/off time to calculate the total screen time. Won't even need the screen time permission. 😅

Ya that will work fine when you just want to track whole time but myself I am also displaying each apps usage time.

Screenshot_20250224-123152

CreativeCodeCat avatar Feb 24 '25 12:02 CreativeCodeCat

Measurements so far (with Opera installed)

13:47 (0 min difference)
Oluncher - Digital Wellbeing
08:46 (2 min difference)
Oluncher - Digital Wellbeing
08:15 (4 min difference)
Oluncher - Digital Wellbeing

OndrejKulhavy avatar Feb 24 '25 13:02 OndrejKulhavy

I installed Opera a few hours ago, and it still works fine. How can I replicate the issue? Could you describe it in more detail? @CreativeCodeCat

OndrejKulhavy avatar Feb 24 '25 13:02 OndrejKulhavy

After a full day of testing on both my phone and the Android emulator, everything is working as expected. The difference is usually just a minute. @tanujnotes, give my latest changes a try!

PS: I have Opera installed, and no problems so far.

    fun getTodaysScreenTime() {
        viewModelScope.launch {
            val startOfToday = Calendar.getInstance().apply {
                timeZone = TimeZone.getDefault()
                set(Calendar.HOUR_OF_DAY, 0)
                set(Calendar.MINUTE, 0)
                set(Calendar.SECOND, 0)
                set(Calendar.MILLISECOND, 0)
            }.timeInMillis

            val currentTime = System.currentTimeMillis()
            val usageStatsManager = appContext.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager
            val packageManager = appContext.packageManager

            // Get launcher apps
            val launcherIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
            val launcherApps = packageManager.queryIntentActivities(launcherIntent, 0)
                .map { it.activityInfo.packageName }
                .toSet()

            val stats = usageStatsManager.queryUsageStats(
                UsageStatsManager.INTERVAL_DAILY,
                startOfToday,
                currentTime
            )

            val today = Calendar.getInstance()
            val todayYear = today.get(Calendar.YEAR)
            val todayDayOfYear = today.get(Calendar.DAY_OF_YEAR)

            val totalTimeInMillis = stats
                .filter { stat ->
                    // Exclude launcher apps
                    !launcherApps.contains(stat.packageName) &&
                            // Filter for today's stats
                            Calendar.getInstance().apply {
                                timeInMillis = stat.firstTimeStamp
                            }.let { statCalendar ->
                                val statYear = statCalendar.get(Calendar.YEAR)
                                val statDayOfYear = statCalendar.get(Calendar.DAY_OF_YEAR)
                                statYear == todayYear && statDayOfYear == todayDayOfYear
                            }
                }
                .sumOf { it.totalTimeInForeground }

            val formattedTime = appContext.formattedTimeSpent(totalTimeInMillis)
            screenTimeValue.postValue(formattedTime)
        }
    }

OndrejKulhavy avatar Feb 24 '25 17:02 OndrejKulhavy

this is if i don't block opera that's why I have it blocked atm.

Launcher (1) is unblocked and Launcher (2) is blocked

Launcher (1) Launcher (2) Digital Wellbeing Digital Wellbeing (Opera)
Launcher  (1) Launcher  (2) Digital Wellbeing Digital Wellbeing (Opera)

CreativeCodeCat avatar Feb 24 '25 17:02 CreativeCodeCat

Interesting! Which version of Opera do you have? Are you referring to the standard Opera browser from Google Play? Are you using any plugins? I’d like to replicate the issue so I can inspect the data.

I do have Opera installed, but as you can see from my previous messages, it doesn’t seem to affect anything.

OndrejKulhavy avatar Feb 24 '25 18:02 OndrejKulhavy

ya just the basic opera from play store no plugins. it could be fairphone tbh.

CreativeCodeCat avatar Feb 24 '25 18:02 CreativeCodeCat

@OndrejKulhavy Have you pushed the latest changes in your repo? Edit: I've copied the code in your comment. Will update tomorrow. Thank you!

tanujnotes avatar Feb 24 '25 19:02 tanujnotes

Yes, all is up to date.

OndrejKulhavy avatar Feb 24 '25 22:02 OndrejKulhavy

Unfortunately, that didn't work for me (Pixel, Android 15). I'm trying an alternate way to get the screen time which shows promise (using UsageEvents instead of UsageStats). Let's see how it goes.

image

tanujnotes avatar Feb 25 '25 09:02 tanujnotes