fix: don't flag another app's earlier alarm in the self-check

The system reports only the earliest alarm clock of any app, so an older
alarm left in the stock clock showed up as a problem the offered fix
couldn't solve. Another app's alarm at or before Clockula's next fire is
now fine; a missing registration, or anything later than ours, still is
a problem.
This commit is contained in:
2026-10-21 17:26:50 +02:00
parent 99b357f4fb
commit 9cd40d6813
3 changed files with 47 additions and 11 deletions
@@ -186,21 +186,24 @@ object SelfCheck {
/**
* Compares Clockula's own computed next fire against what the system
* actually has registered. No alarm scheduled is not a problem; an
* unknown owner is given the benefit of the doubt (the platform does not
* always expose it), but a *confirmed* foreign owner, a missing system
* registration, or a mismatched time all mean the registration this app
* made is not the one the system will honour.
* actually has registered. The system reports only the earliest alarm
* clock of *any* app, so another app's alarm at or before Clockula's hides
* Clockula's registration rather than disproving it, and is not a problem.
* No alarm scheduled is not a problem either. A missing registration, our
* own at the wrong time, or anything later than our next fire all mean the
* registration this app made is not the one the system will honour.
*/
private fun nextAlarm(state: DeviceState): CheckResult {
val expected = state.expectedNextFire
val system = state.systemNextAlarm
val owner = state.systemNextAlarmOwnerPackage
val ok = when {
expected == null -> true
state.systemNextAlarm == null -> false
owner != null && owner != state.ownPackageName -> false
state.systemNextAlarm != expected -> false
else -> true
system == null -> false
system == expected -> true
system > expected -> false
// Earlier than ours: someone else's, unless the owner is confirmed as us.
else -> owner != state.ownPackageName
}
return CheckResult(
SelfCheckId.NEXT_ALARM,
+1 -1
View File
@@ -371,7 +371,7 @@
<string name="selfcheck_oem_warn">Some manufacturers aggressively close background apps — see how to keep Clockula exempt</string>
<string name="selfcheck_next_alarm_title">Next alarm, as the system sees it</string>
<string name="selfcheck_next_alarm_ok">Matches what Clockula scheduled</string>
<string name="selfcheck_next_alarm_ok">Agrees with what Clockula scheduled</string>
<string name="selfcheck_next_alarm_problem">Doesn\'t match what Clockula scheduled — open the app to re-check</string>
<!-- M10: widgets -->
@@ -189,7 +189,18 @@ class SelfCheckTest {
}
@Test
fun `the system's registration belonging to a different app is a problem`() {
fun `another app's alarm before Clockula's is fine, since it hides ours`() {
val state = allClear.copy(
expectedNextFire = Instant.fromEpochMilliseconds(2_000_000),
systemNextAlarm = Instant.fromEpochMilliseconds(1_000_000),
systemNextAlarmOwnerPackage = "com.other.clock",
)
assertThat(resultFor(state, SelfCheckId.NEXT_ALARM).status).isEqualTo(CheckStatus.OK)
}
@Test
fun `another app's alarm at the same instant as Clockula's is fine`() {
val at = Instant.fromEpochMilliseconds(1_000_000)
val state = allClear.copy(
expectedNextFire = at,
@@ -197,6 +208,28 @@ class SelfCheckTest {
systemNextAlarmOwnerPackage = "com.other.clock",
)
assertThat(resultFor(state, SelfCheckId.NEXT_ALARM).status).isEqualTo(CheckStatus.OK)
}
@Test
fun `another app's alarm after Clockula's means ours is missing`() {
val state = allClear.copy(
expectedNextFire = Instant.fromEpochMilliseconds(1_000_000),
systemNextAlarm = Instant.fromEpochMilliseconds(2_000_000),
systemNextAlarmOwnerPackage = "com.other.clock",
)
assertThat(resultFor(state, SelfCheckId.NEXT_ALARM).status).isEqualTo(CheckStatus.PROBLEM)
}
@Test
fun `our own registration earlier than expected is a problem`() {
val state = allClear.copy(
expectedNextFire = Instant.fromEpochMilliseconds(2_000_000),
systemNextAlarm = Instant.fromEpochMilliseconds(1_000_000),
systemNextAlarmOwnerPackage = OWN_PACKAGE,
)
assertThat(resultFor(state, SelfCheckId.NEXT_ALARM).status).isEqualTo(CheckStatus.PROBLEM)
}