From 04fcf5cc29c760c786b0685c12986093689e8d56 Mon Sep 17 00:00:00 2001 From: frankpreel Date: Mon, 26 May 2025 10:37:36 +0200 Subject: [PATCH 1/3] fix : Despite a screen time set user can exceed the limit REF: https://gitlab.e.foundation/e/os/backlog/-/issues/3348 --- app/src/main/AndroidManifest.xml | 10 +++ .../e/parentalcontrol/AlarmReceiver.kt | 3 +- .../e/parentalcontrol/DeviceAdmin.kt | 3 + .../MidnightTriggerReceiver.kt | 66 +++++++++++++++++++ .../e/parentalcontrol/StealthActivity.kt | 35 ++++++++++ .../e/parentalcontrol/UsageStatsManager.kt | 2 +- 6 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt create mode 100644 app/src/main/java/foundation/e/parentalcontrol/StealthActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d316cc4..538a16b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -55,6 +55,16 @@ + + + + = allowedScreenTime - WHEN_ACTIVATE_POPUP_IN_MS) { if (!popupHasAlreadyBeenActive) { sendPopup(context) - popupHasAlreadyBeenActive } - stats.activeAlarm(context, (allowedScreenTime - res), popupHasAlreadyBeenActive) + stats.activeAlarm(context, (allowedScreenTime - res), true) } else if (res < allowedScreenTime) { blockApp(false, context) stats.activeAlarm( diff --git a/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt b/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt index 328a816..9628dbf 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt @@ -26,6 +26,7 @@ import android.os.Handler import android.os.Looper import android.util.Log import android.widget.Toast +import foundation.e.parentalcontrol.MidnightTriggerReceiver.AlarmUtils.scheduleMidnightTrigger import foundation.e.parentalcontrol.data.DnsManager import foundation.e.parentalcontrol.ui.view.MainUI import foundation.e.parentalcontrol.utils.Constants @@ -69,6 +70,8 @@ class DeviceAdmin : DeviceAdminReceiver() { PrefsUtils.init(context) val stats = UsageStatsManager() stats.activeAlarm(context, 0, false) + + scheduleMidnightTrigger(context) PrefsUtils.restoreAllowedAppList(context) } } diff --git a/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt b/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt new file mode 100644 index 0000000..654aec3 --- /dev/null +++ b/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 MURENA SAS + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ +package foundation.e.parentalcontrol + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import java.util.Calendar + +class MidnightTriggerReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val stealthIntent = + Intent(context, StealthActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + context.startActivity(stealthIntent) + + AlarmUtils.scheduleMidnightTrigger(context) + } + + object AlarmUtils { + fun scheduleMidnightTrigger(context: Context) { + val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val intent = Intent(context, MidnightTriggerReceiver::class.java) + val pendingIntent = + PendingIntent.getBroadcast( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val calendar = + Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 1) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + if (before(Calendar.getInstance())) add(Calendar.DAY_OF_MONTH, 1) + } + + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + calendar.timeInMillis, + pendingIntent + ) + } + } +} diff --git a/app/src/main/java/foundation/e/parentalcontrol/StealthActivity.kt b/app/src/main/java/foundation/e/parentalcontrol/StealthActivity.kt new file mode 100644 index 0000000..fccb985 --- /dev/null +++ b/app/src/main/java/foundation/e/parentalcontrol/StealthActivity.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2025 MURENA SAS + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ +package foundation.e.parentalcontrol + +import android.app.Activity +import android.os.Bundle + +/* + Stealth Activity + Briefly displays something invisible at midnight to handle the special case of an activity (like video) + without user interaction. + + In order to have an event at this date in UsageStatsManager so as to be able to count down the times correctly. +*/ +class StealthActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + finish() + } +} diff --git a/app/src/main/java/foundation/e/parentalcontrol/UsageStatsManager.kt b/app/src/main/java/foundation/e/parentalcontrol/UsageStatsManager.kt index fb8e628..da9d39f 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/UsageStatsManager.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/UsageStatsManager.kt @@ -172,7 +172,7 @@ class UsageStatsManager { if (statusList.isEmpty()) { return false } - val lastStatus = statusList.get(statusList.lastIndex) + val lastStatus = statusList.last() if (!lastStatus.activated) { return false } -- GitLab From 0385c6c4c895ed46072cece813b6283495c4d473 Mon Sep 17 00:00:00 2001 From: frankpreel Date: Mon, 26 May 2025 10:39:59 +0200 Subject: [PATCH 2/3] fix : Adding screen time debug info --- .../java/foundation/e/parentalcontrol/MainActivity.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/parentalcontrol/MainActivity.kt b/app/src/main/java/foundation/e/parentalcontrol/MainActivity.kt index 8b8bad5..9d9579f 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/MainActivity.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/MainActivity.kt @@ -30,6 +30,7 @@ import android.os.Handler import android.os.Looper import android.os.UserManager import android.provider.Settings +import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler @@ -149,6 +150,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { + private val TAG = "MainActivity" private var mActivity: ComponentActivity = this private val dA: DeviceAdmin = DeviceAdmin() @@ -164,11 +166,12 @@ class MainActivity : ComponentActivity() { private fun onStartUp() { + val stats = UsageStatsManager() + val totalUsageMillis = stats.showTime(mActivity) + val totalUsageMinutes = totalUsageMillis.milliseconds.toDouble(DurationUnit.MINUTES) + if (BuildConfig.DEBUG) { lifecycleScope.launch { - val stats = UsageStatsManager() - val totalUsageMillis = stats.showTime(mActivity) - val totalUsageMinutes = totalUsageMillis.milliseconds.toDouble(DurationUnit.MINUTES) Toast.makeText( mActivity, "Usage: ${totalUsageMinutes.roundToInt()} min", @@ -176,6 +179,8 @@ class MainActivity : ComponentActivity() { ) .show() } + } else { + Log.d(TAG, "Usage: ${totalUsageMinutes.roundToInt()} min") } if (intent?.action == KEY_PARENTAL_CONTROL_AUTHENTICATE) { -- GitLab From 2102a37a7e97b1408e6b350eaa77f8103aa400a3 Mon Sep 17 00:00:00 2001 From: frankpreel Date: Mon, 26 May 2025 11:31:15 +0200 Subject: [PATCH 3/3] fix : Move AlarmUtils to specific file --- .../e/parentalcontrol/DeviceAdmin.kt | 4 +- .../MidnightTriggerReceiver.kt | 33 +----------- .../e/parentalcontrol/utils/AlarmUtils.kt | 54 +++++++++++++++++++ 3 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/foundation/e/parentalcontrol/utils/AlarmUtils.kt diff --git a/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt b/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt index 9628dbf..6ff44a8 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/DeviceAdmin.kt @@ -26,9 +26,9 @@ import android.os.Handler import android.os.Looper import android.util.Log import android.widget.Toast -import foundation.e.parentalcontrol.MidnightTriggerReceiver.AlarmUtils.scheduleMidnightTrigger import foundation.e.parentalcontrol.data.DnsManager import foundation.e.parentalcontrol.ui.view.MainUI +import foundation.e.parentalcontrol.utils.AlarmUtils import foundation.e.parentalcontrol.utils.Constants import foundation.e.parentalcontrol.utils.Constants.BLOCK_ALL_SYSTEM_APP import foundation.e.parentalcontrol.utils.Constants.DELAY_WAIT_OWNER_ACCESS @@ -71,7 +71,7 @@ class DeviceAdmin : DeviceAdminReceiver() { val stats = UsageStatsManager() stats.activeAlarm(context, 0, false) - scheduleMidnightTrigger(context) + AlarmUtils.scheduleMidnightTrigger(context) PrefsUtils.restoreAllowedAppList(context) } } diff --git a/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt b/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt index 654aec3..a362b13 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/MidnightTriggerReceiver.kt @@ -17,12 +17,10 @@ */ package foundation.e.parentalcontrol -import android.app.AlarmManager -import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import java.util.Calendar +import foundation.e.parentalcontrol.utils.AlarmUtils class MidnightTriggerReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -34,33 +32,4 @@ class MidnightTriggerReceiver : BroadcastReceiver() { AlarmUtils.scheduleMidnightTrigger(context) } - - object AlarmUtils { - fun scheduleMidnightTrigger(context: Context) { - val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val intent = Intent(context, MidnightTriggerReceiver::class.java) - val pendingIntent = - PendingIntent.getBroadcast( - context, - 0, - intent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - - val calendar = - Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 1) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - if (before(Calendar.getInstance())) add(Calendar.DAY_OF_MONTH, 1) - } - - alarmManager.setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, - calendar.timeInMillis, - pendingIntent - ) - } - } } diff --git a/app/src/main/java/foundation/e/parentalcontrol/utils/AlarmUtils.kt b/app/src/main/java/foundation/e/parentalcontrol/utils/AlarmUtils.kt new file mode 100644 index 0000000..a5767ba --- /dev/null +++ b/app/src/main/java/foundation/e/parentalcontrol/utils/AlarmUtils.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 MURENA SAS + * + * 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, either version 3 of the License, or + * (at your option) any later version. + * + * 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 . + * + */ +package foundation.e.parentalcontrol.utils + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import foundation.e.parentalcontrol.MidnightTriggerReceiver +import java.util.Calendar + +object AlarmUtils { + fun scheduleMidnightTrigger(context: Context) { + val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val intent = Intent(context, MidnightTriggerReceiver::class.java) + val pendingIntent = + PendingIntent.getBroadcast( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val calendar = + Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 1) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + if (before(Calendar.getInstance())) add(Calendar.DAY_OF_MONTH, 1) + } + + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + calendar.timeInMillis, + pendingIntent + ) + } +} -- GitLab