docs: apply surgical corrections for correctness and clarity
Analysis doc improvements: - Add accuracy note for Capacitor vs Cordova runtime naming - Declare 5 required Status Matrix fields verbatim in Bridge Surface - Add Manifest Hygiene checklist to Build Configuration section Implementation plan improvements: - Fix BOOT_COMPLETED permission wiring (top-level permissions, not receiver attribute) - Add user-visible Exact-Alarm Decision Rule for QA/ops reasoning - Add 2 ops-grade error cases: E_CHANNEL_MISSING, E_BAD_CONFIG - Add Preflight Golden Path (Demo) to Runbooks for 30-second sanity checks - Clamp text lengths at JS boundary with unknown field rejection - Declare minimal Event IDs for deterministic grep operations All changes maintain existing structure with surgical precision edits.
This commit is contained in:
@@ -184,6 +184,8 @@ assets/public/
|
||||
├── index.html # Main test interface (549 lines)
|
||||
├── capacitor.js # Capacitor runtime
|
||||
├── capacitor_plugins.js # Plugin JavaScript bridge
|
||||
|
||||
> **Note:** On pure Capacitor builds, the runtime is `capacitor.js`. Only include `cordova.js/cordova_plugins.js` if Cordova-compat is enabled; otherwise remove those references for accuracy.
|
||||
└── plugins/ # Plugin JavaScript files
|
||||
```
|
||||
|
||||
@@ -363,6 +365,14 @@ android {
|
||||
**Purpose**: Auto-generated Capacitor configuration
|
||||
**Note**: Regenerated on each `npx cap sync` - should not be manually edited
|
||||
|
||||
**Manifest Hygiene (Quick Scan)**
|
||||
- [ ] `<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>`
|
||||
- [ ] `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` (if you truly need exact)
|
||||
- [ ] `<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>`
|
||||
- [ ] BootReceiver: `exported="true"` + BOOT_COMPLETED filter
|
||||
- [ ] Other receivers exported=false unless needed
|
||||
- [ ] No stray `android:permission=` on BootReceiver
|
||||
|
||||
## Runtime Behavior
|
||||
|
||||
### App Startup Sequence
|
||||
@@ -533,6 +543,8 @@ This architecture serves as both a practical testing tool and a reference implem
|
||||
- `requestNotificationPermissions() -> {granted: boolean, permissions: {...}}`
|
||||
- `getNotificationStatus() -> {isEnabled, isScheduled, nextNotificationTime, ...}`
|
||||
|
||||
**Status Matrix MUST include:** `postNotificationsGranted`, `exactAlarmGranted`, `channelEnabled`, `batteryOptimizationsIgnored`, `canScheduleNow`.
|
||||
|
||||
## Permission & Settings Truth Table
|
||||
|
||||
| Symptom | Likely Cause | Action |
|
||||
|
||||
@@ -230,6 +230,10 @@ public class ScheduleDaily {
|
||||
- **Error Handling**: Standardized error responses
|
||||
- **Validation**: Input validation before processing
|
||||
|
||||
### Exact-Alarm Decision Rule (User-Visible)
|
||||
If `SCHEDULE_EXACT_ALARM` is **granted** → schedule with `setExactAndAllowWhileIdle`.
|
||||
If **denied or quota-limited** → schedule via WorkManager (exp backoff + jitter) and surface `E_EXACT_ALARM_DENIED` (with "Degraded timing — Doze may delay" hint).
|
||||
|
||||
### 3. Service Locator
|
||||
|
||||
**Purpose**: Dependency injection for testability
|
||||
@@ -413,12 +417,12 @@ export class SchemaValidator {
|
||||
errors.push('Time must be in HH:mm format');
|
||||
}
|
||||
|
||||
// Validate title length
|
||||
// Validate title length (enforce exactly: title ≤ 100 chars)
|
||||
if (request.title && request.title.length > 100) {
|
||||
errors.push('Title must be 100 characters or less');
|
||||
}
|
||||
|
||||
// Validate body length
|
||||
// Validate body length (enforce exactly: body ≤ 500 chars)
|
||||
if (request.body && request.body.length > 500) {
|
||||
errors.push('Body must be 500 characters or less');
|
||||
}
|
||||
@@ -433,9 +437,17 @@ export class SchemaValidator {
|
||||
errors.push('Priority must be low, default, or high');
|
||||
}
|
||||
|
||||
// Reject unknown fields
|
||||
const allowedFields = ['time', 'title', 'body', 'sound', 'priority'];
|
||||
const unknownFields = Object.keys(request).filter(key => !allowedFields.includes(key));
|
||||
if (unknownFields.length > 0) {
|
||||
errors.push(`Unknown fields: ${unknownFields.join(', ')}`);
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors
|
||||
errors,
|
||||
message: errors.join('; ') // Single joined message for UI display
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -495,6 +507,11 @@ public class SecureNetworkClient {
|
||||
#### Implementation Plan
|
||||
```xml
|
||||
<!-- AndroidManifest.xml -->
|
||||
<!-- Top-level permissions -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<receiver
|
||||
android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
|
||||
android:enabled="true"
|
||||
@@ -507,8 +524,7 @@ public class SecureNetworkClient {
|
||||
<receiver
|
||||
android:name="com.timesafari.dailynotification.BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
@@ -809,6 +825,12 @@ interface ScheduleResponse {
|
||||
- Implement progress logging
|
||||
- Create log export functionality
|
||||
|
||||
**Event IDs (minimum set)**
|
||||
- EVT_SCHEDULE_REQUEST / EVT_SCHEDULE_OK / EVT_SCHEDULE_FAIL
|
||||
- EVT_BOOT_REHYDRATE_START / EVT_BOOT_REHYDRATE_DONE
|
||||
- EVT_CHANNEL_STATUS / EVT_PERM_STATUS / EVT_EXACT_ALARM_STATUS
|
||||
- EVT_DOZE_FALLBACK_TAKEN / EVT_WORKER_RETRY
|
||||
|
||||
### Phase 3: Security & Performance
|
||||
- [ ] **Security Hardening**
|
||||
- Add network security measures
|
||||
@@ -941,6 +963,8 @@ By following this plan, the test app will become more maintainable, reliable, an
|
||||
| E_CHANNEL_DISABLED | Channel blocked/low importance | Open channel settings |
|
||||
| E_EXACT_ALARM_DENIED | No exact alarm | Open exact alarm settings / fallback |
|
||||
| E_DOZE_LIMIT | Throttled by Doze | Expect delays; fallback taken |
|
||||
| E_CHANNEL_MISSING | Channel ID not found at runtime | Recreate channel; verify ID & importance |
|
||||
| E_BAD_CONFIG | Missing/invalid plugin config at startup | Check `capacitor.config.json` and diagnostics dump |
|
||||
|
||||
## Runbooks
|
||||
|
||||
@@ -952,3 +976,10 @@ Check storage for orphan schedule rows; verify idempotent rescheduler logs.
|
||||
|
||||
### Silent notifications
|
||||
Verify channel importance and OEM-specific "Heads-up" settings.
|
||||
|
||||
### Preflight Golden Path (Demo)
|
||||
1) Open app → run "Comprehensive Status" → all five fields green.
|
||||
2) Tap "Open Channel Settings" → ensure importance = High.
|
||||
3) Tap "Open Exact Alarm Settings" → grant if available.
|
||||
4) Run "Immediate Notification" → toast & notif appear within 5s.
|
||||
5) Schedule HH:mm+5 → lock screen → delivery within ±1m (exact) or delayed (fallback).
|
||||
|
||||
Reference in New Issue
Block a user