Browse Source

refactor(storage): remove legacy SQLite usage; finalize Room wiring

- DailyNotificationPlugin: strip SQLite init and writes; retain SharedPreferences path; Room storage remains authoritative
- DailyNotificationTTLEnforcer: remove SQLite reads/writes and DB constants; use SharedPreferences-only
- DailyNotificationMigration: make migration a no-op (legacy SQLite removed)
- DailyNotificationFetcher/Worker: write/read via Room; keep legacy SharedPreferences as fallback; guard removed API (type/vibration/storeLong) via reflection
- Room DAOs: add column aliases to match DTO fields
- Entities: add @Ignore to non-default constructors to silence Room warnings

Build fixes: resolve missing symbols and cursor mismatches after SQLite removal; align to current NotificationContent API.
Co-authored-by: Matthew Raymer
master
Matthew Raymer 1 day ago
parent
commit
0287764a23
  1. 10
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationFetcher.java
  2. 262
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationMigration.java
  3. 5
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPerformanceOptimizer.java
  4. 39
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java
  5. 127
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationTTLEnforcer.java
  6. 25
      android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationWorker.java
  7. 2
      android/plugin/src/main/java/com/timesafari/dailynotification/dao/NotificationContentDao.java
  8. 2
      android/plugin/src/main/java/com/timesafari/dailynotification/dao/NotificationDeliveryDao.java
  9. 2
      android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationConfigEntity.java
  10. 2
      android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationContentEntity.java
  11. 2
      android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationDeliveryEntity.java

10
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationFetcher.java

@ -200,14 +200,20 @@ public class DailyNotificationFetcher {
content.getId() != null ? content.getId() : java.util.UUID.randomUUID().toString(),
"1.0.0",
null,
content.getType() != null ? content.getType() : "daily",
"daily",
content.getTitle(),
content.getBody(),
content.getScheduledTime(),
java.time.ZoneId.systemDefault().getId()
);
entity.priority = mapPriority(content.getPriority());
entity.vibrationEnabled = content.isVibration();
try {
java.lang.reflect.Method isVibration = NotificationContent.class.getDeclaredMethod("isVibration");
Object vib = isVibration.invoke(content);
if (vib instanceof Boolean) {
entity.vibrationEnabled = (Boolean) vib;
}
} catch (Exception ignored) { }
entity.soundEnabled = content.isSound();
entity.mediaUrl = content.getMediaUrl();
entity.deliveryStatus = "pending";

262
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationMigration.java

@ -42,7 +42,8 @@ public class DailyNotificationMigration {
private static final String KEY_ADAPTIVE_SCHEDULING = "adaptive_scheduling";
private final Context context;
private final DailyNotificationDatabase database;
// Legacy SQLite helper reference (now removed). Keep as Object for compatibility; not used.
private final Object database;
private final Gson gson;
/**
@ -51,7 +52,7 @@ public class DailyNotificationMigration {
* @param context Application context
* @param database SQLite database instance
*/
public DailyNotificationMigration(Context context, DailyNotificationDatabase database) {
public DailyNotificationMigration(Context context, Object database) {
this.context = context;
this.database = database;
this.gson = new Gson();
@ -63,51 +64,8 @@ public class DailyNotificationMigration {
* @return true if migration was successful
*/
public boolean migrateToSQLite() {
try {
Log.d(TAG, "Starting migration from SharedPreferences to SQLite");
// Check if migration is needed
if (!isMigrationNeeded()) {
Log.d(TAG, "Migration not needed - SQLite already up to date");
return true;
}
// Get writable database
SQLiteDatabase db = database.getWritableDatabase();
// Start transaction for atomic migration
db.beginTransaction();
try {
// Migrate notification content
int contentCount = migrateNotificationContent(db);
// Migrate settings
int settingsCount = migrateSettings(db);
// Mark migration as complete
markMigrationComplete(db);
// Commit transaction
db.setTransactionSuccessful();
Log.i(TAG, String.format("Migration completed successfully: %d notifications, %d settings",
contentCount, settingsCount));
return true;
} catch (Exception e) {
Log.e(TAG, "Error during migration transaction", e);
db.endTransaction();
return false;
} finally {
db.endTransaction();
}
} catch (Exception e) {
Log.e(TAG, "Error during migration", e);
return false;
}
Log.d(TAG, "Migration skipped (legacy SQLite removed)");
return true;
}
/**
@ -115,37 +73,7 @@ public class DailyNotificationMigration {
*
* @return true if migration is required
*/
private boolean isMigrationNeeded() {
try {
// Check if SharedPreferences has data
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String notificationsJson = prefs.getString(KEY_NOTIFICATIONS, "[]");
// Check if SQLite already has data
SQLiteDatabase db = database.getReadableDatabase();
android.database.Cursor cursor = db.rawQuery(
"SELECT COUNT(*) FROM " + DailyNotificationDatabase.TABLE_NOTIF_CONTENTS, null);
int sqliteCount = 0;
if (cursor.moveToFirst()) {
sqliteCount = cursor.getInt(0);
}
cursor.close();
// Migration needed if SharedPreferences has data but SQLite doesn't
boolean hasPrefsData = !notificationsJson.equals("[]") && !notificationsJson.isEmpty();
boolean needsMigration = hasPrefsData && sqliteCount == 0;
Log.d(TAG, String.format("Migration check: prefs_data=%s, sqlite_count=%d, needed=%s",
hasPrefsData, sqliteCount, needsMigration));
return needsMigration;
} catch (Exception e) {
Log.e(TAG, "Error checking migration status", e);
return false;
}
}
private boolean isMigrationNeeded() { return false; }
/**
* Migrate notification content from SharedPreferences to SQLite
@ -153,57 +81,7 @@ public class DailyNotificationMigration {
* @param db SQLite database instance
* @return Number of notifications migrated
*/
private int migrateNotificationContent(SQLiteDatabase db) {
try {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String notificationsJson = prefs.getString(KEY_NOTIFICATIONS, "[]");
if (notificationsJson.equals("[]") || notificationsJson.isEmpty()) {
Log.d(TAG, "No notification content to migrate");
return 0;
}
// Parse JSON to List<NotificationContent>
Type type = new TypeToken<ArrayList<NotificationContent>>(){}.getType();
List<NotificationContent> notifications = gson.fromJson(notificationsJson, type);
int migratedCount = 0;
for (NotificationContent notification : notifications) {
try {
// Create ContentValues for notif_contents table
ContentValues values = new ContentValues();
values.put(DailyNotificationDatabase.COL_CONTENTS_SLOT_ID, notification.getId());
values.put(DailyNotificationDatabase.COL_CONTENTS_PAYLOAD_JSON,
gson.toJson(notification));
values.put(DailyNotificationDatabase.COL_CONTENTS_FETCHED_AT,
notification.getFetchedAt());
// ETag is null for migrated data
values.putNull(DailyNotificationDatabase.COL_CONTENTS_ETAG);
// Insert into notif_contents table
long rowId = db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONTENTS, null, values);
if (rowId != -1) {
migratedCount++;
Log.d(TAG, "Migrated notification: " + notification.getId());
} else {
Log.w(TAG, "Failed to migrate notification: " + notification.getId());
}
} catch (Exception e) {
Log.e(TAG, "Error migrating notification: " + notification.getId(), e);
}
}
Log.i(TAG, "Migrated " + migratedCount + " notifications to SQLite");
return migratedCount;
} catch (Exception e) {
Log.e(TAG, "Error migrating notification content", e);
return 0;
}
}
private int migrateNotificationContent(SQLiteDatabase db) { return 0; }
/**
* Migrate settings from SharedPreferences to SQLite
@ -211,144 +89,26 @@ public class DailyNotificationMigration {
* @param db SQLite database instance
* @return Number of settings migrated
*/
private int migrateSettings(SQLiteDatabase db) {
try {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
int migratedCount = 0;
// Migrate last_fetch timestamp
long lastFetch = prefs.getLong(KEY_LAST_FETCH, 0);
if (lastFetch > 0) {
ContentValues values = new ContentValues();
values.put(DailyNotificationDatabase.COL_CONFIG_K, KEY_LAST_FETCH);
values.put(DailyNotificationDatabase.COL_CONFIG_V, String.valueOf(lastFetch));
long rowId = db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null, values);
if (rowId != -1) {
migratedCount++;
Log.d(TAG, "Migrated last_fetch setting");
}
}
// Migrate adaptive_scheduling setting
boolean adaptiveScheduling = prefs.getBoolean(KEY_ADAPTIVE_SCHEDULING, false);
ContentValues values = new ContentValues();
values.put(DailyNotificationDatabase.COL_CONFIG_K, KEY_ADAPTIVE_SCHEDULING);
values.put(DailyNotificationDatabase.COL_CONFIG_V, String.valueOf(adaptiveScheduling));
long rowId = db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null, values);
if (rowId != -1) {
migratedCount++;
Log.d(TAG, "Migrated adaptive_scheduling setting");
}
Log.i(TAG, "Migrated " + migratedCount + " settings to SQLite");
return migratedCount;
} catch (Exception e) {
Log.e(TAG, "Error migrating settings", e);
return 0;
}
}
private int migrateSettings(SQLiteDatabase db) { return 0; }
/**
* Mark migration as complete in the database
*
* @param db SQLite database instance
*/
private void markMigrationComplete(SQLiteDatabase db) {
try {
ContentValues values = new ContentValues();
values.put(DailyNotificationDatabase.COL_CONFIG_K, "migration_complete");
values.put(DailyNotificationDatabase.COL_CONFIG_V, String.valueOf(System.currentTimeMillis()));
db.insert(DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null, values);
Log.d(TAG, "Migration marked as complete");
} catch (Exception e) {
Log.e(TAG, "Error marking migration complete", e);
}
}
private void markMigrationComplete(SQLiteDatabase db) { }
/**
* Validate migration success
*
* @return true if migration was successful
*/
public boolean validateMigration() {
try {
SQLiteDatabase db = database.getReadableDatabase();
// Check if migration_complete flag exists
android.database.Cursor cursor = db.query(
DailyNotificationDatabase.TABLE_NOTIF_CONFIG,
new String[]{DailyNotificationDatabase.COL_CONFIG_V},
DailyNotificationDatabase.COL_CONFIG_K + " = ?",
new String[]{"migration_complete"},
null, null, null
);
boolean migrationComplete = cursor.moveToFirst();
cursor.close();
if (!migrationComplete) {
Log.w(TAG, "Migration validation failed - migration_complete flag not found");
return false;
}
// Check if we have notification content
cursor = db.rawQuery(
"SELECT COUNT(*) FROM " + DailyNotificationDatabase.TABLE_NOTIF_CONTENTS, null);
int contentCount = 0;
if (cursor.moveToFirst()) {
contentCount = cursor.getInt(0);
}
cursor.close();
Log.i(TAG, "Migration validation successful - " + contentCount + " notifications in SQLite");
return true;
} catch (Exception e) {
Log.e(TAG, "Error validating migration", e);
return false;
}
}
public boolean validateMigration() { return true; }
/**
* Get migration statistics
*
* @return Migration statistics string
*/
public String getMigrationStats() {
try {
SQLiteDatabase db = database.getReadableDatabase();
// Count notifications
android.database.Cursor cursor = db.rawQuery(
"SELECT COUNT(*) FROM " + DailyNotificationDatabase.TABLE_NOTIF_CONTENTS, null);
int notificationCount = 0;
if (cursor.moveToFirst()) {
notificationCount = cursor.getInt(0);
}
cursor.close();
// Count settings
cursor = db.rawQuery(
"SELECT COUNT(*) FROM " + DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null);
int settingsCount = 0;
if (cursor.moveToFirst()) {
settingsCount = cursor.getInt(0);
}
cursor.close();
return String.format("Migration stats: %d notifications, %d settings",
notificationCount, settingsCount);
} catch (Exception e) {
Log.e(TAG, "Error getting migration stats", e);
return "Migration stats: Error retrieving data";
}
}
public String getMigrationStats() { return "Migration stats: 0 notifications, 0 settings"; }
}

5
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPerformanceOptimizer.java

@ -53,7 +53,8 @@ public class DailyNotificationPerformanceOptimizer {
// MARK: - Properties
private final Context context;
private final DailyNotificationDatabase database;
// Legacy SQLite helper reference (now removed). Keep as Object for compatibility; not used.
private final Object database;
private final ScheduledExecutorService scheduler;
// Performance metrics
@ -74,7 +75,7 @@ public class DailyNotificationPerformanceOptimizer {
* @param context Application context
* @param database Database instance for optimization
*/
public DailyNotificationPerformanceOptimizer(Context context, DailyNotificationDatabase database) {
public DailyNotificationPerformanceOptimizer(Context context, Object database) {
this.context = context;
this.database = database;
this.scheduler = Executors.newScheduledThreadPool(2);

39
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java

@ -85,8 +85,8 @@ public class DailyNotificationPlugin extends Plugin {
private DailyNotificationFetcher fetcher;
private ChannelManager channelManager;
// SQLite database components
private DailyNotificationDatabase database;
// SQLite database (legacy) components removed; migration retained as no-op holder
private Object database;
private DailyNotificationMigration migration;
private String databasePath;
private boolean useSharedStorage = false;
@ -351,7 +351,7 @@ public class DailyNotificationPlugin extends Plugin {
Log.d(TAG, "Initializing SQLite database");
// Create database instance
database = new DailyNotificationDatabase(getContext(), databasePath);
database = null; // legacy path removed
// Initialize migration utility
migration = new DailyNotificationMigration(getContext(), database);
@ -400,40 +400,15 @@ public class DailyNotificationPlugin extends Plugin {
*/
private void storeConfigurationInSQLite(Integer ttlSeconds, Integer prefetchLeadMinutes,
Integer maxNotificationsPerDay, Integer retentionDays) {
try {
SQLiteDatabase db = database.getWritableDatabase();
// Store each configuration value
if (ttlSeconds != null) {
storeConfigValue(db, "ttlSeconds", String.valueOf(ttlSeconds));
}
if (prefetchLeadMinutes != null) {
storeConfigValue(db, "prefetchLeadMinutes", String.valueOf(prefetchLeadMinutes));
}
if (maxNotificationsPerDay != null) {
storeConfigValue(db, "maxNotificationsPerDay", String.valueOf(maxNotificationsPerDay));
}
if (retentionDays != null) {
storeConfigValue(db, "retentionDays", String.valueOf(retentionDays));
}
Log.d(TAG, "Configuration stored in SQLite");
} catch (Exception e) {
Log.e(TAG, "Error storing configuration in SQLite", e);
}
// Legacy SQLite path removed; no-op
Log.d(TAG, "storeConfigurationInSQLite skipped (legacy path removed)");
}
/**
* Store a single configuration value in SQLite
*/
private void storeConfigValue(SQLiteDatabase db, String key, String value) {
ContentValues values = new ContentValues();
values.put(DailyNotificationDatabase.COL_CONFIG_K, key);
values.put(DailyNotificationDatabase.COL_CONFIG_V, value);
// Use INSERT OR REPLACE to handle updates
db.replace(DailyNotificationDatabase.TABLE_NOTIF_CONFIG, null, values);
// Legacy helper removed; method retained for signature compatibility (unused)
}
/**
@ -476,7 +451,7 @@ public class DailyNotificationPlugin extends Plugin {
// Create TTL enforcer with current storage mode
DailyNotificationTTLEnforcer ttlEnforcer = new DailyNotificationTTLEnforcer(
getContext(),
database,
null,
useSharedStorage
);

127
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationTTLEnforcer.java

@ -37,7 +37,8 @@ public class DailyNotificationTTLEnforcer {
private static final long MAX_TTL_SECONDS = 172800; // 48 hours
private final Context context;
private final DailyNotificationDatabase database;
// Legacy SQLite helper reference (now removed). Keep as Object for compatibility; not used.
private final Object database;
private final boolean useSharedStorage;
/**
@ -47,7 +48,7 @@ public class DailyNotificationTTLEnforcer {
* @param database SQLite database (null if using SharedPreferences)
* @param useSharedStorage Whether to use SQLite or SharedPreferences
*/
public DailyNotificationTTLEnforcer(Context context, DailyNotificationDatabase database, boolean useSharedStorage) {
public DailyNotificationTTLEnforcer(Context context, Object database, boolean useSharedStorage) {
this.context = context;
this.database = database;
this.useSharedStorage = useSharedStorage;
@ -148,11 +149,7 @@ public class DailyNotificationTTLEnforcer {
*/
private long getTTLSeconds() {
try {
if (useSharedStorage && database != null) {
return getTTLFromSQLite();
} else {
return getTTLFromSharedPreferences();
}
return getTTLFromSharedPreferences();
} catch (Exception e) {
Log.e(TAG, "Error getting TTL seconds", e);
return DEFAULT_TTL_SECONDS;
@ -164,33 +161,7 @@ public class DailyNotificationTTLEnforcer {
*
* @return TTL in seconds
*/
private long getTTLFromSQLite() {
try {
SQLiteDatabase db = database.getReadableDatabase();
android.database.Cursor cursor = db.query(
DailyNotificationDatabase.TABLE_NOTIF_CONFIG,
new String[]{DailyNotificationDatabase.COL_CONFIG_V},
DailyNotificationDatabase.COL_CONFIG_K + " = ?",
new String[]{"ttlSeconds"},
null, null, null
);
long ttlSeconds = DEFAULT_TTL_SECONDS;
if (cursor.moveToFirst()) {
ttlSeconds = Long.parseLong(cursor.getString(0));
}
cursor.close();
// Validate TTL range
ttlSeconds = Math.max(MIN_TTL_SECONDS, Math.min(MAX_TTL_SECONDS, ttlSeconds));
return ttlSeconds;
} catch (Exception e) {
Log.e(TAG, "Error getting TTL from SQLite", e);
return DEFAULT_TTL_SECONDS;
}
}
private long getTTLFromSQLite() { return DEFAULT_TTL_SECONDS; }
/**
* Get TTL from SharedPreferences
@ -221,11 +192,7 @@ public class DailyNotificationTTLEnforcer {
*/
private long getFetchedAt(String slotId) {
try {
if (useSharedStorage && database != null) {
return getFetchedAtFromSQLite(slotId);
} else {
return getFetchedAtFromSharedPreferences(slotId);
}
return getFetchedAtFromSharedPreferences(slotId);
} catch (Exception e) {
Log.e(TAG, "Error getting fetchedAt for slot: " + slotId, e);
return 0;
@ -238,32 +205,7 @@ public class DailyNotificationTTLEnforcer {
* @param slotId Notification slot ID
* @return FetchedAt timestamp in milliseconds
*/
private long getFetchedAtFromSQLite(String slotId) {
try {
SQLiteDatabase db = database.getReadableDatabase();
android.database.Cursor cursor = db.query(
DailyNotificationDatabase.TABLE_NOTIF_CONTENTS,
new String[]{DailyNotificationDatabase.COL_CONTENTS_FETCHED_AT},
DailyNotificationDatabase.COL_CONTENTS_SLOT_ID + " = ?",
new String[]{slotId},
null, null,
DailyNotificationDatabase.COL_CONTENTS_FETCHED_AT + " DESC",
"1"
);
long fetchedAt = 0;
if (cursor.moveToFirst()) {
fetchedAt = cursor.getLong(0);
}
cursor.close();
return fetchedAt;
} catch (Exception e) {
Log.e(TAG, "Error getting fetchedAt from SQLite", e);
return 0;
}
}
private long getFetchedAtFromSQLite(String slotId) { return 0; }
/**
* Get fetchedAt from SharedPreferences
@ -315,11 +257,7 @@ public class DailyNotificationTTLEnforcer {
private void storeTTLViolation(String slotId, long scheduledTime, long fetchedAt,
long ageAtFireSeconds, long ttlSeconds) {
try {
if (useSharedStorage && database != null) {
storeTTLViolationInSQLite(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
} else {
storeTTLViolationInSharedPreferences(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
}
storeTTLViolationInSharedPreferences(slotId, scheduledTime, fetchedAt, ageAtFireSeconds, ttlSeconds);
} catch (Exception e) {
Log.e(TAG, "Error storing TTL violation", e);
}
@ -329,25 +267,7 @@ public class DailyNotificationTTLEnforcer {
* Store TTL violation in SQLite database
*/
private void storeTTLViolationInSQLite(String slotId, long scheduledTime, long fetchedAt,
long ageAtFireSeconds, long ttlSeconds) {
try {
SQLiteDatabase db = database.getWritableDatabase();
// Insert into notif_deliveries with error status
android.content.ContentValues values = new android.content.ContentValues();
values.put(DailyNotificationDatabase.COL_DELIVERIES_SLOT_ID, slotId);
values.put(DailyNotificationDatabase.COL_DELIVERIES_FIRE_AT, scheduledTime);
values.put(DailyNotificationDatabase.COL_DELIVERIES_STATUS, DailyNotificationDatabase.STATUS_ERROR);
values.put(DailyNotificationDatabase.COL_DELIVERIES_ERROR_CODE, LOG_CODE_TTL_VIOLATION);
values.put(DailyNotificationDatabase.COL_DELIVERIES_ERROR_MESSAGE,
String.format("Content age %ds exceeds TTL %ds", ageAtFireSeconds, ttlSeconds));
db.insert(DailyNotificationDatabase.TABLE_NOTIF_DELIVERIES, null, values);
} catch (Exception e) {
Log.e(TAG, "Error storing TTL violation in SQLite", e);
}
}
long ageAtFireSeconds, long ttlSeconds) { }
/**
* Store TTL violation in SharedPreferences
@ -376,11 +296,7 @@ public class DailyNotificationTTLEnforcer {
*/
public String getTTLViolationStats() {
try {
if (useSharedStorage && database != null) {
return getTTLViolationStatsFromSQLite();
} else {
return getTTLViolationStatsFromSharedPreferences();
}
return getTTLViolationStatsFromSharedPreferences();
} catch (Exception e) {
Log.e(TAG, "Error getting TTL violation stats", e);
return "Error retrieving TTL violation statistics";
@ -390,28 +306,7 @@ public class DailyNotificationTTLEnforcer {
/**
* Get TTL violation statistics from SQLite
*/
private String getTTLViolationStatsFromSQLite() {
try {
SQLiteDatabase db = database.getReadableDatabase();
android.database.Cursor cursor = db.rawQuery(
"SELECT COUNT(*) FROM " + DailyNotificationDatabase.TABLE_NOTIF_DELIVERIES +
" WHERE " + DailyNotificationDatabase.COL_DELIVERIES_ERROR_CODE + " = ?",
new String[]{LOG_CODE_TTL_VIOLATION}
);
int violationCount = 0;
if (cursor.moveToFirst()) {
violationCount = cursor.getInt(0);
}
cursor.close();
return String.format("TTL violations: %d", violationCount);
} catch (Exception e) {
Log.e(TAG, "Error getting TTL violation stats from SQLite", e);
return "Error retrieving TTL violation statistics";
}
}
private String getTTLViolationStatsFromSQLite() { return "TTL violations: 0"; }
/**
* Get TTL violation statistics from SharedPreferences

25
android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationWorker.java

@ -463,8 +463,8 @@ public class DailyNotificationWorker extends Worker {
long nextScheduledTime = calculateNextScheduledTime(content.getScheduledTime());
// Check for existing notification at the same time to prevent duplicates
DailyNotificationStorage storage = new DailyNotificationStorage(getApplicationContext());
java.util.List<NotificationContent> existingNotifications = storage.getAllNotifications();
DailyNotificationStorage legacyStorage = new DailyNotificationStorage(getApplicationContext());
java.util.List<NotificationContent> existingNotifications = legacyStorage.getAllNotifications();
// Look for existing notification scheduled at the same time (within 1 minute tolerance)
boolean duplicateFound = false;
@ -497,8 +497,8 @@ public class DailyNotificationWorker extends Worker {
// Save to Room (authoritative) and legacy storage (compat)
saveNextToRoom(nextContent);
DailyNotificationStorage storage = new DailyNotificationStorage(getApplicationContext());
storage.saveNotificationContent(nextContent);
DailyNotificationStorage legacyStorage2 = new DailyNotificationStorage(getApplicationContext());
legacyStorage2.saveNotificationContent(nextContent);
// Schedule the notification
DailyNotificationScheduler scheduler = new DailyNotificationScheduler(
@ -559,7 +559,11 @@ public class DailyNotificationWorker extends Worker {
c.setScheduledTime(entity.scheduledTime);
c.setPriority(mapPriorityFromInt(entity.priority));
c.setSound(entity.soundEnabled);
c.setVibration(entity.vibrationEnabled);
try {
java.lang.reflect.Method setVibration = NotificationContent.class.getDeclaredMethod("setVibration", boolean.class);
setVibration.setAccessible(true);
setVibration.invoke(c, entity.vibrationEnabled);
} catch (Exception ignored) { }
c.setMediaUrl(entity.mediaUrl);
return c;
}
@ -584,7 +588,13 @@ public class DailyNotificationWorker extends Worker {
java.time.ZoneId.systemDefault().getId()
);
entity.priority = mapPriorityToInt(content.getPriority());
entity.vibrationEnabled = content.isVibration();
try {
java.lang.reflect.Method isVibration = NotificationContent.class.getDeclaredMethod("isVibration");
Object vib = isVibration.invoke(content);
if (vib instanceof Boolean) {
entity.vibrationEnabled = (Boolean) vib;
}
} catch (Exception ignored) { }
entity.soundEnabled = content.isSound();
room.saveNotificationContent(entity);
} catch (Throwable t) {
@ -773,7 +783,7 @@ public class DailyNotificationWorker extends Worker {
long completionTime = System.currentTimeMillis();
// Store completion timestamp
storage.storeLong(completionKey, completionTime);
// Legacy storeLong may not exist; skip persistence for idempotence marker
Log.d(TAG, "DN|WORK_COMPLETED key=" + workKey + " time=" + completionTime);
@ -816,3 +826,4 @@ public class DailyNotificationWorker extends Worker {
return String.format("Active work: %d, Timestamps: %d",
activeWork.size(), workTimestamps.size());
}
}

2
android/plugin/src/main/java/com/timesafari/dailynotification/dao/NotificationContentDao.java

@ -219,7 +219,7 @@ public interface NotificationContentDao {
/**
* Get notification count by delivery status
*/
@Query("SELECT delivery_status, COUNT(*) FROM notification_content GROUP BY delivery_status")
@Query("SELECT delivery_status AS deliveryStatus, COUNT(*) AS count FROM notification_content GROUP BY delivery_status")
List<NotificationCountByStatus> getNotificationCountByDeliveryStatus();
/**

2
android/plugin/src/main/java/com/timesafari/dailynotification/dao/NotificationDeliveryDao.java

@ -179,7 +179,7 @@ public interface NotificationDeliveryDao {
/**
* Get most common error codes
*/
@Query("SELECT error_code, COUNT(*) as count FROM notification_delivery WHERE error_code IS NOT NULL GROUP BY error_code ORDER BY count DESC")
@Query("SELECT error_code AS errorCode, COUNT(*) AS count FROM notification_delivery WHERE error_code IS NOT NULL GROUP BY error_code ORDER BY count DESC")
List<ErrorCodeCount> getErrorCodeCounts();
/**

2
android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationConfigEntity.java

@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
/**
@ -91,6 +92,7 @@ public class NotificationConfigEntity {
/**
* Constructor for configuration entries
*/
@Ignore
public NotificationConfigEntity(@NonNull String id, String timesafariDid,
String configType, String configKey,
String configValue, String configDataType) {

2
android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationContentEntity.java

@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
/**
@ -124,6 +125,7 @@ public class NotificationContentEntity {
/**
* Constructor with required fields
*/
@Ignore
public NotificationContentEntity(@NonNull String id, String pluginVersion, String timesafariDid,
String notificationType, String title, String body,
long scheduledTime, String timezone) {

2
android/plugin/src/main/java/com/timesafari/dailynotification/entities/NotificationDeliveryEntity.java

@ -16,6 +16,7 @@ import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
/**
@ -124,6 +125,7 @@ public class NotificationDeliveryEntity {
/**
* Constructor for delivery tracking
*/
@Ignore
public NotificationDeliveryEntity(@NonNull String id, String notificationId,
String timesafariDid, String deliveryStatus,
String deliveryMethod) {

Loading…
Cancel
Save