fix: refresh UI on app resume after force-stop recovery

- Add visibility change handler to refresh UI when app becomes visible
- Check for recent notifications and show indicator if notification was received
- Update lastKnownNextNotificationTime on app resume
- Auto-enable fire verification in Test 1 if alarm is within 5 minutes

Previously, after force-stop recovery:
- UI only refreshed on page load (which doesn't fire again after force-stop)
- Notification received indicator didn't show if notification fired while app was stopped
- Test 1 didn't verify that restored alarms actually fire

The fix:
- Listen for visibilitychange event to detect app resume
- Refresh all status displays (plugin, permissions, channel) when app becomes visible
- Check for notifications received in last 2 minutes and show indicator
- Wait 1 second after visibility change to allow recovery to complete
- Test 1 now automatically verifies restored alarms fire if within 5 minutes

This ensures the UI stays in sync after force-stop recovery and shows
notification indicators for notifications that fired while the app was stopped.
This commit is contained in:
Matthew Raymer
2025-12-30 08:58:11 +00:00
parent 4c1281754e
commit d913f03e23
2 changed files with 97 additions and 28 deletions

View File

@@ -1221,43 +1221,62 @@ main() {
evidence_block "test1_force_stop_recovery" evidence_block "test1_force_stop_recovery"
# Optional: Verify alarm fires (controlled by VERIFY_FIRE flag) # Verify alarm fires if it's scheduled within reasonable time window (< 5 minutes)
if [ "${VERIFY_FIRE}" = "true" ] && [ -n "${alarm_trigger_ms}" ] && [ "${goto_test1_end}" != "true" ]; then # This ensures restored alarms actually work, not just that they were restored
substep "Step 8: Verify alarm fires at scheduled time (optional)" if [ -n "${alarm_trigger_ms}" ] && [ "${goto_test1_end}" != "true" ]; then
local current_time_sec current_time_ms wait_ms wait_sec local current_time_sec current_time_ms wait_ms wait_sec
current_time_sec=$(get_current_time) current_time_sec=$(get_current_time)
current_time_ms=$((current_time_sec * 1000)) current_time_ms=$((current_time_sec * 1000))
wait_ms=$((alarm_trigger_ms - current_time_ms)) wait_ms=$((alarm_trigger_ms - current_time_ms))
if [ "${wait_ms}" -lt 0 ]; then # Auto-enable fire verification if alarm is within 5 minutes (300 seconds)
warn "Alarm time already passed (${wait_ms} ms ago); skipping fire verification" # Or if VERIFY_FIRE is explicitly set to true
else local should_verify_fire=false
wait_sec=$((wait_ms / 1000)) if [ "${VERIFY_FIRE}" = "true" ]; then
should_verify_fire=true
elif [ "${wait_ms}" -gt 0 ] && [ "${wait_ms}" -lt 300000 ]; then
# Alarm is in the future and within 5 minutes - auto-verify
should_verify_fire=true
fi
if [ "${should_verify_fire}" = "true" ]; then
substep "Step 8: Verify restored alarm fires at scheduled time"
set_test_context "phase1" "phase1_test1" "p1_t1_s6"
# Clamp upper bound to prevent accidentally waiting 30+ minutes if [ "${wait_ms}" -lt 0 ]; then
if [ "${wait_sec}" -gt 600 ]; then step_warn "p1_t1_s6" "Alarm time already passed"
warn "Alarm is >10 minutes away (${wait_sec}s); skipping fire verification" warn "Alarm time already passed (${wait_ms} ms ago); cannot verify fire"
info "To test fire verification, schedule alarm closer to current time"
else else
info "Alarm scheduled for: ${alarm_readable}" wait_sec=$((wait_ms / 1000))
info "Current time: $(date -d "@${current_time_sec}" 2>/dev/null || echo "${current_time_sec}")"
info "Waiting ~${wait_sec} seconds for alarm to fire..."
clear_logs # Clamp upper bound to prevent accidentally waiting too long
sleep ${wait_sec} if [ "${wait_sec}" -gt 600 ]; then
sleep 2 step_warn "p1_t1_s6" "Alarm too far in future"
warn "Alarm is >10 minutes away (${wait_sec}s); skipping fire verification"
info "Checking logs for fired alarm..." info "To test fire verification, schedule alarm closer to current time"
local alarm_fired
alarm_fired="$($ADB_BIN logcat -d | grep -E "DNP-RECEIVE|DNP-NOTIFY|DNP-WORK|Alarm fired|Notification displayed" | tail -10)"
if [ -n "${alarm_fired}" ]; then
ok "Alarm fired! Logs:"
echo "${alarm_fired}"
else else
warn "No alarm fire logs found" step_start "p1_t1_s6" "Waiting for restored alarm to fire"
info "Check notification tray manually or review recent logs" info "Restored alarm scheduled for: ${alarm_readable}"
info "Current time: $(date -d "@${current_time_sec}" 2>/dev/null || echo "${current_time_sec}")"
info "Waiting ~${wait_sec} seconds for restored alarm to fire..."
clear_logs
sleep ${wait_sec}
sleep 2
info "Checking logs for fired alarm..."
local alarm_fired
alarm_fired="$($ADB_BIN logcat -d | grep -E "DNP-RECEIVE|DNP-NOTIFY|DNP-WORK|Alarm fired|Notification displayed" | tail -10)"
if [ -n "${alarm_fired}" ]; then
step_pass "p1_t1_s6" "Restored alarm fired successfully"
ok "✅ Restored alarm fired! Logs:"
echo "${alarm_fired}"
else
step_fail "p1_t1_s6" "Restored alarm did not fire"
warn "⚠️ No alarm fire logs found - restored alarm may not have fired"
info "Check notification tray manually or review recent logs"
fi
fi fi
fi fi
fi fi

View File

@@ -590,6 +590,56 @@
}, 500); }, 500);
}); });
// Refresh UI when app comes back to foreground (after force-stop, app resume, etc.)
// This ensures the UI updates after recovery from force-stop
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
console.log('[Visibility] App became visible, refreshing UI status...');
// Small delay to allow recovery to complete
setTimeout(() => {
loadPluginStatus();
loadPermissionStatus();
loadChannelStatus();
// Also check for recent notifications that might have been missed
if (window.DailyNotification) {
window.DailyNotification.getNotificationStatus()
.then(result => {
// Check if a notification was received recently (within last 2 minutes)
if (result.lastNotificationTime) {
const lastTime = new Date(result.lastNotificationTime);
const now = new Date();
const timeDiff = now - lastTime;
if (timeDiff > 0 && timeDiff < 120000) {
console.log('[Visibility] Recent notification detected, showing indicator');
const indicator = document.getElementById('notificationReceivedIndicator');
const timeSpan = document.getElementById('notificationReceivedTime');
if (indicator && timeSpan) {
indicator.style.display = 'block';
lastTime.setSeconds(0, 0);
timeSpan.textContent = `Received at ${lastTime.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true })}`;
// Hide after 30 seconds
setTimeout(() => {
indicator.style.display = 'none';
}, 30000);
}
}
}
// Update last known next notification time
lastKnownNextNotificationTime = result.nextNotificationTime;
})
.catch(error => {
console.error('[Visibility] Failed to get notification status:', error);
});
}
}, 1000); // Wait 1 second for recovery to complete
}
});
console.log('Functions attached to window:', { console.log('Functions attached to window:', {