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.
This commit is contained in:
310
android/DATABASE_CONSOLIDATION_PLAN.md
Normal file
310
android/DATABASE_CONSOLIDATION_PLAN.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user