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.
311 lines
10 KiB
Markdown
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
|
|
|