Files
daily-notification-plugin/android/DATABASE_CONSOLIDATION_PLAN.md
Matthew Raymer 18106e5ba8 feat(android): consolidate databases and add prefetch scheduling
Consolidate Java and Kotlin database implementations into unified
schema, add delayed prefetch scheduling, and fix notification
delivery issues.

Database Consolidation:
- Merge Java DailyNotificationDatabase into Kotlin DatabaseSchema
- Add migration path from v1 to v2 unified schema
- Include all entities: ContentCache, Schedule, Callback, History,
  NotificationContentEntity, NotificationDeliveryEntity,
  NotificationConfigEntity
- Add @JvmStatic getInstance() for Java interoperability
- Update DailyNotificationWorker and DailyNotificationStorageRoom
  to use unified database

Prefetch Functionality:
- Add scheduleDelayedFetch() to FetchWorker for 5-minute prefetch
  before notifications
- Support delayed WorkManager scheduling with initialDelay
- Update scheduleDailyNotification() to optionally schedule prefetch
  when URL is provided

Notification Delivery Fixes:
- Register NotifyReceiver in AndroidManifest.xml (was missing,
  causing notifications not to fire)
- Add safe database initialization with lazy getDatabase() helper
- Prevent PluginLoadException on database init failure

Build Configuration:
- Add kotlin-android and kotlin-kapt plugins
- Configure Room annotation processor (kapt) for Kotlin
- Add Room KTX dependency for coroutines support
- Fix Gradle settings with pluginManagement blocks

Plugin Methods Added:
- checkPermissionStatus() - detailed permission status
- requestNotificationPermissions() - request POST_NOTIFICATIONS
- scheduleDailyNotification() - schedule with AlarmManager
- configureNativeFetcher() - configure native content fetcher
- Various status and configuration methods

Code Cleanup:
- Remove duplicate BootReceiver.java (keep Kotlin version)
- Remove duplicate DailyNotificationPlugin.java (keep Kotlin version)
- Remove old Java database implementation
- Add native fetcher SPI registry (@JvmStatic methods)

The unified database ensures schedule persistence across reboots
and provides a single source of truth for all plugin data.
Prefetch scheduling enables content caching before notifications
fire, improving offline-first reliability.
2025-11-06 06:28:00 +00:00

311 lines
10 KiB
Markdown

# Database Consolidation Plan
## Current State
### Database 1: Java (`daily_notification_plugin.db`)
- `notification_content` - Specific notification instances
- `notification_delivery` - Delivery tracking/analytics
- `notification_config` - Configuration
### Database 2: Kotlin (`daily_notification_database`)
- `content_cache` - Fetched content with TTL
- `schedules` - Recurring schedule patterns (CRITICAL for reboot)
- `callbacks` - Callback configurations
- `history` - Execution history
## Unified Schema Design
### Required Tables (All Critical)
1. **`schedules`** - Recurring schedule patterns
- Stores cron/clockTime patterns
- Used to restore schedules after reboot
- Fields: id, kind ('fetch'/'notify'), cron, clockTime, enabled, lastRunAt, nextRunAt, jitterMs, backoffPolicy, stateJson
2. **`content_cache`** - Fetched content with TTL
- Stores prefetched content for offline-first display
- Fields: id, fetchedAt, ttlSeconds, payload (BLOB), meta
3. **`notification_config`** - Plugin configuration
- Stores user preferences and plugin settings
- Fields: id, timesafariDid, configType, configKey, configValue, configDataType, isEncrypted, createdAt, updatedAt
4. **`callbacks`** - Callback configurations
- Stores callback endpoint configurations
- Fields: id, kind ('http'/'local'/'queue'), target, headersJson, enabled, createdAt
### Optional Tables (Analytics/Debugging)
5. **`notification_content`** - Specific notification instances
- May still be needed for one-time notifications or TimeSafari integration
- Fields: All existing fields from Java entity
6. **`notification_delivery`** - Delivery tracking
- Analytics for delivery attempts and user interactions
- Fields: All existing fields from Java entity
7. **`history`** - Execution history
- Logs fetch/notify/callback execution
- Fields: id, refId, kind, occurredAt, durationMs, outcome, diagJson
## Consolidation Strategy
- [x] Keep Kotlin schema as base - It already has critical tables
- [x] Add Java tables to Kotlin schema - Merge missing entities
- [x] Update all Java code - Use unified database instance
- [x] Update all Kotlin code - Use unified database instance
- [x] Single database file: `daily_notification_plugin.db`
## Migration Path
- [x] Create unified `DailyNotificationDatabase` with all entities
- [x] Update Java code to use unified database
- [x] Update Kotlin code to use unified database
- [x] Remove old `DailyNotificationDatabase` files
- [ ] Test reboot recovery
## Key Decisions
- **Primary language**: Kotlin (more modern, better coroutine support)
- **Database name**: `daily_notification_plugin.db` (Java naming convention)
- **All entities**: Both Java and Kotlin compatible
- **DAOs**: Mix of Java and Kotlin DAOs as needed
## TypeScript Interface Requirements
Since the plugin owns the database, the host app/webview needs TypeScript interfaces to read/write data.
### Required TypeScript Methods
#### Schedules Management
```typescript
// Read schedules
getSchedules(options?: { kind?: 'fetch' | 'notify', enabled?: boolean }): Promise<Schedule[]>
getSchedule(id: string): Promise<Schedule | null>
// Write schedules
createSchedule(schedule: CreateScheduleInput): Promise<Schedule>
updateSchedule(id: string, updates: Partial<Schedule>): Promise<Schedule>
deleteSchedule(id: string): Promise<void>
enableSchedule(id: string, enabled: boolean): Promise<void>
// Utility
calculateNextRunTime(schedule: string): Promise<number>
```
#### Content Cache Management
```typescript
// Read content cache
getContentCache(options?: { id?: string }): Promise<ContentCache | null>
getLatestContentCache(): Promise<ContentCache | null>
getContentCacheHistory(limit?: number): Promise<ContentCache[]>
// Write content cache
saveContentCache(content: CreateContentCacheInput): Promise<ContentCache>
clearContentCache(options?: { olderThan?: number }): Promise<void>
```
#### Configuration Management
```typescript
// Read config
getConfig(key: string, options?: { timesafariDid?: string }): Promise<Config | null>
getAllConfigs(options?: { timesafariDid?: string, configType?: string }): Promise<Config[]>
// Write config
setConfig(config: CreateConfigInput): Promise<Config>
updateConfig(key: string, value: string, options?: { timesafariDid?: string }): Promise<Config>
deleteConfig(key: string, options?: { timesafariDid?: string }): Promise<void>
```
#### Callbacks Management
```typescript
// Read callbacks
getCallbacks(options?: { enabled?: boolean }): Promise<Callback[]>
getCallback(id: string): Promise<Callback | null>
// Write callbacks
registerCallback(callback: CreateCallbackInput): Promise<Callback>
updateCallback(id: string, updates: Partial<Callback>): Promise<Callback>
deleteCallback(id: string): Promise<void>
enableCallback(id: string, enabled: boolean): Promise<void>
```
#### History/Analytics (Optional)
```typescript
// Read history
getHistory(options?: {
since?: number,
kind?: 'fetch' | 'notify' | 'callback',
limit?: number
}): Promise<History[]>
getHistoryStats(): Promise<HistoryStats>
```
### Type Definitions
```typescript
interface Schedule {
id: string
kind: 'fetch' | 'notify'
cron?: string
clockTime?: string // HH:mm format
enabled: boolean
lastRunAt?: number
nextRunAt?: number
jitterMs: number
backoffPolicy: string
stateJson?: string
}
interface ContentCache {
id: string
fetchedAt: number
ttlSeconds: number
payload: string // Base64 or JSON string
meta?: string
}
interface Config {
id: string
timesafariDid?: string
configType: string
configKey: string
configValue: string
configDataType: string
isEncrypted: boolean
createdAt: number
updatedAt: number
}
interface Callback {
id: string
kind: 'http' | 'local' | 'queue'
target: string
headersJson?: string
enabled: boolean
createdAt: number
}
interface History {
id: number
refId: string
kind: 'fetch' | 'notify' | 'callback' | 'boot_recovery'
occurredAt: number
durationMs?: number
outcome: string
diagJson?: string
}
```
# Database Consolidation Plan
## Status: ✅ **CONSOLIDATION COMPLETE**
The unified database has been successfully created and all code has been migrated to use it.
## Current State
### Unified Database (`daily_notification_plugin.db`)
Located in: `android/src/main/java/com/timesafari/dailynotification/DatabaseSchema.kt`
**All Tables Consolidated:**
-`content_cache` - Fetched content with TTL (Kotlin)
-`schedules` - Recurring schedule patterns (Kotlin, CRITICAL for reboot)
-`callbacks` - Callback configurations (Kotlin)
-`history` - Execution history (Kotlin)
-`notification_content` - Specific notification instances (Java)
-`notification_delivery` - Delivery tracking/analytics (Java)
-`notification_config` - Configuration management (Java)
### Old Database Files (DEPRECATED - REMOVED)
-`android/src/main/java/com/timesafari/dailynotification/database/DailyNotificationDatabase.java` - **REMOVED** - All functionality merged into unified database
## Migration Status
### ✅ Completed Tasks
- [x] Analyzed both database schemas and identified all required tables
- [x] Designed unified database schema with all required entities
- [x] Created unified DailyNotificationDatabase class (Kotlin)
- [x] Added migration from version 1 (Kotlin-only) to version 2 (unified)
- [x] Updated all Java code to use unified database
- [x] `DailyNotificationStorageRoom.java` - Uses unified database
- [x] `DailyNotificationWorker.java` - Uses unified database
- [x] Updated all Kotlin code to use unified database
- [x] `DailyNotificationPlugin.kt` - Uses unified database
- [x] `FetchWorker.kt` - Uses unified database
- [x] `NotifyReceiver.kt` - Uses unified database
- [x] `BootReceiver.kt` - Uses unified database
- [x] Implemented all Config methods in PluginMethods
- [x] TypeScript interfaces updated for database CRUD operations
- [x] Documentation created for AI assistants
### ⏳ Pending Tasks
- [x] Remove old database files (`DailyNotificationDatabase.java`)
- [ ] Test reboot recovery with unified database
- [ ] Verify migration path works correctly
## Unified Schema Design (IMPLEMENTED)
### Required Tables (All Critical)
1. **`schedules`** - Recurring schedule patterns
- Stores cron/clockTime patterns
- Used to restore schedules after reboot
- Fields: id, kind ('fetch'/'notify'), cron, clockTime, enabled, lastRunAt, nextRunAt, jitterMs, backoffPolicy, stateJson
2. **`content_cache`** - Fetched content with TTL
- Stores prefetched content for offline-first display
- Fields: id, fetchedAt, ttlSeconds, payload (BLOB), meta
3. **`notification_config`** - Plugin configuration
- Stores user preferences and plugin settings
- Fields: id, timesafariDid, configType, configKey, configValue, configDataType, isEncrypted, createdAt, updatedAt, ttlSeconds, isActive, metadata
4. **`callbacks`** - Callback configurations
- Stores callback endpoint configurations
- Fields: id, kind ('http'/'local'/'queue'), target, headersJson, enabled, createdAt
5. **`notification_content`** - Specific notification instances
- Stores notification content with plugin-specific fields
- Fields: All existing fields from Java entity
6. **`notification_delivery`** - Delivery tracking
- Analytics for delivery attempts and user interactions
- Fields: All existing fields from Java entity
7. **`history`** - Execution history
- Logs fetch/notify/callback execution
- Fields: id, refId, kind, occurredAt, durationMs, outcome, diagJson
## Implementation Details
### Database Access
- **Kotlin**: `DailyNotificationDatabase.getDatabase(context)`
- **Java**: `DailyNotificationDatabase.getInstance(context)` (Java-compatible wrapper)
### Migration Path
- Version 1 → Version 2: Automatically creates Java entity tables when upgrading from Kotlin-only schema
- Migration runs automatically on first access after upgrade
### Thread Safety
- All database operations use Kotlin coroutines (`Dispatchers.IO`)
- Room handles thread safety internally
- Singleton pattern ensures single database instance
## Next Steps
1. **Remove Old Database File** ✅ COMPLETE
- [x] Delete `android/src/main/java/com/timesafari/dailynotification/database/DailyNotificationDatabase.java`
- [x] Verify no remaining references
2. **Testing**
- [ ] Test reboot recovery with unified database
- [ ] Verify schedule restoration works correctly
- [ ] Verify all Config methods work correctly
- [ ] Test migration from v1 to v2
3. **Documentation**
- [ ] Update any remaining documentation references
- [ ] Verify AI documentation is complete