- Android: move plugin source to org/timesafari/dailynotification, update
namespace, manifest package, and all package/imports; change intent actions
to org.timesafari.daily.NOTIFICATION and DISMISS
- iOS: update bundle IDs, BGTask identifiers, subsystem labels, and queue
names in Plugin and Xcode projects
- Capacitor: update plugin class registration and appIds in configs
- Test apps (android-test-app, daily-notification-test, ios-test-app):
applicationId/bundleId, manifests, ProGuard, scripts, and docs
- Docs: bulk update references; add CONSUMING_APP_MIGRATION_COM_TO_ORG.md
for consuming app migration
BREAKING CHANGE: Consuming apps must update plugin class to
org.timesafari.dailynotification.DailyNotificationPlugin, manifest
receivers/actions, and iOS BGTask identifiers per migration doc.
EMULATOR_GUIDE.md:
- Add "Checking and Installing Prerequisites" (how to check Node, npm, Java,
ANDROID_HOME, adb, emulator, AVDs; install steps; reference to
scripts/check-environment.js)
- Use API 35 and Pixel8_API35 throughout to match project compileSdk/targetSdk
- Document arm64-v8a for Apple Silicon and x86_64 for Intel; add
troubleshooting for "x86_64 not supported on aarch64 host"
- Bump version to 1.1.0 and last-updated date
test-apps/daily-notification-test/scripts/build.sh:
- When building only Android (--android / --run-android), run
cap:sync:android instead of cap:sync so iOS pod install is skipped and
Android build/run succeeds without fixing the iOS Podfile
The cap:sync:ios npm script was failing with "pod: command not found"
because npm scripts don't inherit the same PATH environment as shell
scripts, preventing detection of pod installed via rbenv shims.
Added pod-install.sh wrapper script that:
- Detects pod via command -v or rbenv shims ($HOME/.rbenv/shims/pod)
- Executes pod install with the found command
- Matches the pod detection logic used in build-native.sh
Updated cap:sync:ios script to use the wrapper instead of calling
pod install directly, ensuring consistent pod detection across
all build contexts.
Fixed build failures when test app dependencies aren't installed and when
Capacitor files don't exist during initial install.
Changes:
- Use `npx run-p` in test app build script to work without local install
- Add dependency check in build-native.sh before building Vue app
- Make fix-capacitor-plugins.js resilient to missing files during postinstall
- Gracefully handles missing capacitor.plugins.json on first install
- Provides clear messaging about when fixes will be applied
- No longer exits with error when Capacitor hasn't been synced yet
This allows the build to complete successfully even when:
- npm dependencies aren't installed in test app
- Capacitor files don't exist yet (will be created during cap:sync:ios)
Files modified:
- scripts/build-native.sh
- test-apps/daily-notification-test/package.json
- test-apps/daily-notification-test/scripts/fix-capacitor-plugins.js
The pending field in NotificationStatus is a boolean, but the UI
was displaying it directly, causing "true" to appear instead of
a numeric count when notifications were scheduled.
Added a pendingCount computed property that converts boolean
values to numbers (true → 1, false → 0) while also handling
number types for future compatibility.
Created standardized StatusList component to eliminate duplicate status
display code across views. Standardized styling:
- Flexbox layout with space-between justification
- Border-bottom dividers (removed via :last-child)
- Optional status-based color coding via show-status-colors prop
- Consistent padding (12px 0) and spacing
Migrated HomeView and StatusView to use the new component:
- HomeView: Replaced inline status list, removed ~50 lines of duplicate CSS
- StatusView: Replaced diagnostics info items, removed ~25 lines of duplicate CSS
- Removed unused helper functions (getStatusType, getStatusDescription)
- Fixed TypeScript type assertions for status values
- Added diagnosticsItems computed property in StatusView
Reduces code duplication by ~75 lines and provides single source of truth
for status list styling across the application.
Replaced StatusCard components with simpler inline list layout:
- Removed card bounding boxes and side padding
- Added horizontal dividers between status items
- Title and value on same line using grid layout (left/right justified)
- Reduced padding and margins for more compact display
- Removed unused StatusCard import
Replace hardcoded '09:00' default with dynamic calculation that sets
the notification time to 3 minutes from now (rounded up to next minute).
This makes it more convenient for users to quickly test notifications
without manually adjusting the time field.
The "Time Until" field in NotificationsView was not updating when
refresh was clicked or over time because the computed property used
Date.now() directly, which is evaluated once and doesn't trigger
reactive updates.
Changes:
- Add reactive currentTime property that updates every second
- Set up interval in mounted() to keep countdown live
- Clean up interval in beforeUnmount() to prevent memory leaks
- Update timeUntilNext computed to use reactive currentTime
- Update refreshNotifications() to immediately refresh currentTime
The countdown now updates automatically every second and immediately
when the refresh button is clicked, without requiring navigation away
and back to the view.
The AppDelegate's willPresent method was not posting the
DailyNotificationDelivered notification event that the plugin
observes to trigger rollover scheduling. This caused rollover
notifications (scheduled 24 hours after the current notification)
to never be created, even though the rollover logic was fully
implemented in the plugin.
The fix extracts notification_id and scheduled_time from the
notification's userInfo and posts them via NotificationCenter using
the decoupled pattern. This allows the plugin to detect notification
delivery and automatically schedule the next day's notification.
Rollover now works correctly: when a notification is delivered,
the plugin schedules the next notification for 24 hours later,
and the NotificationsView properly displays the next notification
timestamp.
Add platform-specific emojis (🤖 Android, 🍎 iOS, 🌐 Web) and status
indicators (✅ Ready, ⚠️ Not Ready, ❓ Unknown) to improve visual
clarity in the home view welcome section.
Replace placeholder content with functional notification status viewer that
displays scheduled notifications and rollover information. Enables verification
of both manually scheduled notifications and automatic rollover scheduling
(24-hour recurrence).
Features:
- Display next scheduled notification time with formatted date/time
- Show time until next notification (days, hours, minutes)
- Display pending notification count
- Show last notification delivery time
- Display rollover status (enabled/disabled, last rollover time) when available
- Additional status info (enabled, scheduled, errors)
- Manual refresh button for status updates
- Loading and error states with platform detection
Uses typed plugin wrapper for type safety with fallback to raw plugin access
for rollover fields not in TypeScript interface (iOS-specific fields).
Replace hardcoded platform values and appStore.platform with
Capacitor.getPlatform() for accurate runtime platform detection.
Changes:
- HomeView: Use Capacitor.getPlatform() instead of appStore.platform
- StatusView: Use Capacitor.getPlatform() for initial diagnostics
- diagnostics-export: Replace hardcoded 'Android' with Capacitor detection
- StatusView: Fix timezone to use actual timezone instead of 'Unknown'
- diagnostics-export: Show 'N/A' for API Level on iOS/web (Android-specific)
Fixes platform badge showing "web" on iOS native and diagnostics
showing incorrect platform information.
The Raw Diagnostics pre element was overflowing its container and
causing the entire page to require horizontal scrolling, making field
values in the diagnostics info section inaccessible.
Added min-width: 0 and overflow: hidden to .diagnostics-json container
to allow proper grid constraint propagation. Added max-width: 100%,
width: 100%, and box-sizing: border-box to .json-output to ensure
it respects container bounds while maintaining horizontal scroll
within the pre element itself.
Add testAlarm() method to iOS plugin for quick notification testing.
Fix plugin method discovery by registering testAlarm in CAPBridgedPlugin
pluginMethods array. Add force-load code in AppDelegate to ensure plugin
is discovered by Capacitor's objc_getClassList scan.
Changes:
- Add testAlarm() implementation in DailyNotificationPlugin.swift
- Register testAlarm in pluginMethods array (required for Capacitor discovery)
- Add force-load code in test app AppDelegate (matches working ios-test-app)
- Add UNUserNotificationCenterDelegate to show notifications in foreground
- Add test notification button to ScheduleView with immediate feedback
- Add debug logging for method discovery and plugin loading
Fixes issue where testAlarm was implemented but returned "UNIMPLEMENTED"
because it wasn't registered in the pluginMethods array. Also ensures
plugin class is loaded before Capacitor's discovery phase.
getExactAlarmStatus() is an Android-only API and was causing
UNIMPLEMENTED errors on iOS. Now only calls the method on Android
platforms, with safe defaults for iOS.
- Check platform before calling getExactAlarmStatus()
- Use default values { enabled: false, supported: false } on iOS
- Add error handling for Android call failures
- Make exact alarm status logging conditional on Android
Create a new RequestPermissionsView that provides a dedicated interface
for checking and requesting notification permissions, matching the
functionality found in the iOS test app.
The view includes:
- Status display with color-coded states (requesting/granted/error)
- Permission status grid showing notifications, exact alarm, and
background refresh status
- Request Permissions button with same functionality as iOS test app
- Platform-specific settings access buttons
- Automatic status refresh on mount
Updated HomeView to navigate to the new view instead of calling
permission request function directly, providing better UX with
dedicated screen for permission management.
- Add back buttons to all sub-views (Schedule, Status, Notifications, History, Logs, Settings, UserZero, About)
- Fix router navigation by importing router instance directly (resolves TypeScript errors with vue-facing-decorator)
- Update back button styling: flex layout with page title, arrow-only label
- Fix "Check Status" action to navigate to StatusView instead of checking status inline
- Remove horizontal padding on mobile views (max-width 768px) for edge-to-edge layout
- Simplify badge styling in HomeView (remove padding and border-radius)
Fixed two build issues preventing Android plugin compilation:
1. Build script now builds from test app context instead of standalone
- Capacitor Android is only available as a project dependency, not from Maven
- Plugin must be built within a Capacitor app's Android project
- Changed build_plugin_for_test_app() to build from test app's android/
directory where Capacitor is available as :capacitor-android project
2. Added JVM arguments for Java 17+ KAPT compatibility
- Java 21's module system blocks KAPT from accessing internal compiler classes
- Added --add-opens flags to both org.gradle.jvmargs and kotlin.daemon.jvmargs
- Kotlin compiler daemon runs separately and needs its own configuration
- Applied to both plugin and test app gradle.properties files
These changes allow the plugin to build successfully with Java 21 and ensure
it's built in the correct context where Capacitor dependencies are available.
Add inline style resets to html, body, and #app elements to
eliminate browser default margins and padding. This ensures consistent layout
baseline across browsers and complements the centralized padding
management in App.vue.
Remove individual padding declarations from view components and
set padding to 0 on App.vue root container. This consolidates
padding management in one place for easier maintenance and
consistent spacing control.
- Update test app index.html with improved UI refresh timing after force-stop
- Includes immediate + delayed refresh pattern for better recovery detection
- Better error handling for database not ready scenarios
- Update test app index.html with JSON.stringify improvements for better log readability
- This file is synced from www/index.html during build process
- Includes date formatting improvements for debugging UI refresh issues
- 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.
- Fix date comparison to check alarm date against current date, not initial alarm date
- Remove false warning when alarm date unchanged but scheduled for future (correct behavior)
- Only warn if alarm date is in the past (actual failure case)
- Improve logic flow: date change confirms rollover, but rollover logs are primary indicator
- When dates match but alarm is scheduled for today/future, defer to rollover log check
This fixes the confusing warning message that appeared when:
- Initial alarm scheduled for tomorrow (2025-12-31)
- Notification fires and rollover reschedules for same tomorrow date
- Rollover logs confirm rollover occurred, but date comparison incorrectly warned
The test now correctly recognizes that an alarm scheduled for the future
is valid, and relies on rollover logs as the primary verification method.
- Fix async/await usage in background fetch handler
- Fix Core Data metadata access errors
- Replace SQLITE_TRANSIENT with nil for Swift compatibility
- Fix PermissionStatus interface and type casts in test app
- Add iOS setup documentation to BUILDING.md
- Update iOS sync workflow to handle Podfile regeneration
Resolves all iOS compilation errors and improves test app setup process.
- Add detailed [UI Refresh] prefix logging to loadPluginStatus()
- Log plugin availability checks, status calls, and UI updates
- Add detailed [Poll] prefix logging to checkNotificationDelivery()
- Log status check results, notification delivery detection, and time comparisons
- Log rollover detection with old/new timestamp details
- Log periodic refresh triggers and initialization of tracking variables
- Include structured object logging for debugging UI refresh behavior
This enables debugging of UI auto-refresh mechanism and visibility
into JavaScript console logs in captured logcat output during test runs.
- Add detailed logging to loadPluginStatus() with [UI Refresh] prefix
- Add detailed logging to checkNotificationDelivery() with [Poll] prefix
- Log status check results, change detection, and refresh triggers
- Log nextNotificationTime comparisons to debug rollover detection
- Include Capacitor/Console in logcat capture pattern to capture JS logs
- Log notification delivery detection and time calculations
- Log when rollover is detected and UI refresh is triggered
This enables debugging of UI auto-refresh mechanism and visibility
into JavaScript console logs in captured logcat output.
- Add TestEventsPlugin for receiving ADB broadcast intents
- Create operator console UI (console/index.html, console.css, console.js)
- Add test plan structure (plan.json) with phases, tests, and steps
- Wire all test scripts (phase1, phase2, phase3) with step context and events
- Add event emission helpers to alarm-test-lib.sh (step_start, step_pass, etc.)
- Update test-phase1.sh with comprehensive prerequisite verification
- Register TestEventsPlugin in capacitor.plugins.json
- Add console documentation (CONSOLE-USAGE.md, CONSOLE-REMAINING-WORK.md)
- Add test implementation alignment tracking (TEST-IMPLEMENTATION-ALIGNMENT.md)
This enables real-time test progress tracking via structured events
from shell scripts to the operator console UI.
- Normalize alarm time seconds to :00 for consistent comparison
- Compare dates (YYYY-MM-DD) instead of full timestamps to detect rollover
- Expand logcat search patterns to catch all rollover logs (DN|RESCHEDULE, etc.)
- Add 5-second wait after notification fire to allow rollover processing
- UI: Normalize seconds display to :00 in all time displays
- UI: Add auto-refresh mechanism that detects nextNotificationTime changes
- UI: Poll every 3 seconds and force refresh when rollover detected
- UI: Initialize tracking variable on page load for change detection
Fixes issue where test passed but alarm time didn't actually change,
and UI wasn't updating to show rescheduled notification time after rollover.
Implement P0-P5 directives for operator clarity, consistent outcomes, and
easy evidence capture across all test phases.
Changes:
- alarm-test-lib.sh: Add evidence collection (capture_alarms, capture_logcat,
capture_screenshot), verdict functions (verdict_pass/warn/fail), run directory
management, and release gating support (RELEASE_GATE_PHASE3)
- test-phase1.sh: Refactor to unified framework with CLI modes (--setup,
--run, --smoke, --all, --ci), micro-prompts, evidence capture, and verdict
blocks for all 5 tests
- test-phase2.sh: Add evidence capture, verdict blocks, and STRICTNESS policy
(soft/hard) for warn vs fail behavior
- test-phase3.sh: Add evidence capture, verdict blocks, release gating
(--gate-phase3), and fatigue reduction (time estimates, automation hints)
- RUNBOOK-TESTING.md: New comprehensive operator guide (669 lines) covering
prerequisites, all phases, evidence locations, verdict interpretation,
common failures, and troubleshooting
All test scripts now use consistent UI helpers (section, substep, info, ok,
warn, error), standardized evidence collection, and clear verdict reporting.
Evidence is saved to timestamped run directories (runs/<RUN_ID>/) with alarms,
logs, and screenshots organized by test phase and scenario.
Tests pass with consistent presentation and reproducible evidence collection.
Moved legacy iOS test harness from ios/App/ to test-apps/ios-app-legacy/
Rationale:
- Test harness should not be in published package tree
- Active test app remains at test-apps/ios-test-app/
- Legacy test harness preserved for reference
Verification:
- Confirmed ios/App no longer appears in npm pack --dry-run
- Build passes successfully
- Package structure cleaner
Progress Docs:
- Updated 00-STATUS.md: marked P1.5b complete
- Updated 01-CHANGELOG-WORK.md: added P1.5b completion entry
Add three test buttons (Empty, Invalid, Negative) below the "Test Notification"
button to test invalid time format handling. Each button attempts to schedule
a notification with invalid values: empty string, "25:00" (invalid hour), and
"-1:30" (negative time).
Improve TEST 3 error detection in test-phase1.sh by:
- Making grep case-insensitive to catch ERROR/invalid patterns
- Adding DNP-* error prefix patterns for plugin error logs
- Documenting that Capacitor bridge errors (⚡️ logs) appear in Xcode console
but not in system logs captured by xcrun simctl
The script was using 'local' keyword outside of function scope,
which caused "local: can only be used in a function" error when
running test-phase2.sh. Removed 'local' from three variable
assignments (device_id and logs) at script top level, as 'local'
is only valid inside functions in bash/zsh.
- Fix getNextNotificationTime() to find earliest scheduled notification
instead of using first request (pendingNotificationRequests doesn't
guarantee order)
- Add comprehensive logging for rollover tracking with DNP-ROLLOVER
prefix for Xcode console filtering
- Reset all notifications and rollover state when scheduling new
notification via scheduleDailyNotification() to ensure clean test
state
- Fix userInfo scope error in handleNotificationDelivery error handler
- Update test app UI to refresh status every 5-10 seconds and
immediately after notification delivery to reflect rollover changes
- Add console logging in UI to debug getNotificationStatus() results
This ensures the UI correctly displays the next notification time after
rollover completes, and test notifications start with a clean slate.
Fixed multiple issues in iOS test script and added logging for test compatibility:
Test Script Fixes:
- Fixed get_simulator_id() to correctly extract UUID from booted devices
- Fixed get_app_logs() to use log show (historical) instead of log stream (live) to avoid hanging
- Improved check_plugin_configured() with multiple detection methods (app container, app listing, data directory)
- Added ensure_plugin_configured() function matching Android pattern for consistent user interaction flow
- Fixed integer comparison error in booted device check (removed newlines from count)
- Removed 'local' keyword from variables in main script body (local can only be used in functions)
- Fixed APP_BUNDLE_ID to match actual bundle identifier
Pending Notification Detection:
- Improved get_pending_notifications() to parse pendingCount from plugin logs
- Added direct log query without restrictive predicate to catch plugin logs
- Added multiple fallback methods for detecting pending count
Plugin Logging Enhancement:
- Added explicit pendingCount logging in DailyNotificationScheduler after scheduling
- Uses both NSLog() and print() to ensure logs appear in system logs and Xcode console
- Matches Android's alarm count logging pattern for test script compatibility
This resolves script crashes and enables reliable detection of pending notifications
for automated testing.
Fixed Swift compilation errors preventing iOS build:
- Added explicit self capture [self] in closures in DailyNotificationReactivationManager
- Removed invalid BGTaskScheduler.shared.registeredTaskIdentifiers API call
- Fixed initialization order in DailyNotificationModel (verifyEntities after container init)
Added missing configureNativeFetcher method to iOS plugin:
- Implemented method matching Android functionality
- Stores configuration in UserDefaults for persistence
- Registered method in pluginMethods array
- Supports both jwtToken and jwtSecret parameters for compatibility
This resolves the runtime error "configureNativeFetcher is not a function"
that was preventing the test app from configuring the plugin.
Adds complete iOS documentation suite to support iOS implementation
parity with Android features. Includes implementation directives,
recovery scenario mappings, database migration guide, troubleshooting
guide, and test scripts.
New Documentation:
- iOS Implementation Directive: Phase-based implementation guide
mirroring Android structure with iOS-specific considerations
- iOS Recovery Scenario Mapping: Maps Android recovery scenarios
to iOS equivalents with detection logic comparisons
- iOS Core Data Migration Guide: Complete Room → Core Data entity
mappings with implementation checklist for missing entities
- iOS Troubleshooting Guide: Common issues, debugging techniques,
and error code reference
Enhanced Documentation:
- API.md: Added iOS-only methods (permissions, background tasks,
pending notifications), platform differences table, and iOS-specific
error types. Updated version to 2.3.0.
Test Infrastructure:
- iOS test scripts for Phase 1 (cold start), Phase 2 (termination),
and Phase 3 (boot recovery) testing scenarios
All documentation addresses gaps identified in iOS Implementation
Documentation Review and provides foundation for iOS recovery feature
implementation (currently pending).
Note: iOS recovery features (ReactivationManager, scenario detection)
are NOT yet implemented. Documentation is ready to guide implementation.