# DailyNotification Plugin - Key Code Snippets for ChatGPT **Created**: 2025-10-14 06:44:58 UTC **Author**: Matthew Raymer ## ๐Ÿ”ง Core Plugin Methods ### **DailyNotificationPlugin.java - Main Methods** ```java @PluginMethod public void scheduleDailyNotification(PluginCall call) { try { Log.d(TAG, "Scheduling daily notification"); ensureStorageInitialized(); String time = call.getString("time", "09:00"); String title = call.getString("title", "Daily Notification"); String body = call.getString("body", "Your daily notification"); boolean sound = call.getBoolean("sound", true); String priority = call.getString("priority", "default"); String url = call.getString("url", ""); // Parse time and schedule notification String[] timeParts = time.split(":"); int hour = Integer.parseInt(timeParts[0]); int minute = Integer.parseInt(timeParts[1]); // Create notification content NotificationContent content = new NotificationContent( UUID.randomUUID().toString(), title, body, System.currentTimeMillis(), sound, priority, url ); // Save to storage storage.saveNotificationContent(content); // Schedule with AlarmManager DailyNotificationScheduler scheduler = new DailyNotificationScheduler(getContext()); scheduler.scheduleNotification(content, hour, minute); JSObject result = new JSObject(); result.put("success", true); result.put("message", "Notification scheduled for " + time); call.resolve(result); } catch (Exception e) { Log.e(TAG, "Error scheduling notification", e); call.reject("Error scheduling notification: " + e.getMessage()); } } private void ensureStorageInitialized() throws Exception { if (storage == null) { Log.w(TAG, "Storage not initialized, initializing now"); storage = new DailyNotificationStorage(getContext()); if (storage == null) { throw new Exception("Failed to initialize storage"); } } } @PluginMethod public void checkAndPerformRecovery(PluginCall call) { try { Log.d(TAG, "Checking for recovery needs"); ensureStorageInitialized(); // Load all saved notifications List savedNotifications = storage.loadAllNotifications(); Log.d(TAG, "Found " + savedNotifications.size() + " saved notifications"); if (savedNotifications.isEmpty()) { Log.d(TAG, "No notifications to recover"); call.resolve(); return; } // Check which notifications need rescheduling DailyNotificationScheduler scheduler = new DailyNotificationScheduler(getContext()); int recoveredCount = 0; for (NotificationContent notification : savedNotifications) { try { // Check if alarm is already scheduled if (!scheduler.isNotificationScheduled(notification.getId())) { // Reschedule the notification scheduler.scheduleNotification(notification); recoveredCount++; Log.d(TAG, "Recovered notification: " + notification.getId()); } } catch (Exception e) { Log.w(TAG, "Failed to recover notification: " + notification.getId(), e); } } Log.i(TAG, "Recovery completed: " + recoveredCount + "/" + savedNotifications.size() + " recovered"); JSObject result = new JSObject(); result.put("recovered", recoveredCount); result.put("total", savedNotifications.size()); call.resolve(result); } catch (Exception e) { Log.e(TAG, "Error during recovery", e); call.reject("Error during recovery: " + e.getMessage()); } } ``` ## ๐Ÿ”„ Boot Recovery System ### **BootReceiver.java - Core Implementation** ```java public class BootReceiver extends BroadcastReceiver { private static final String TAG = "BootReceiver"; private static final String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED"; private static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED"; private static final String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED"; @Override public void onReceive(Context context, Intent intent) { if (intent == null || intent.getAction() == null) { Log.w(TAG, "Received null intent or action"); return; } String action = intent.getAction(); Log.d(TAG, "Received broadcast: " + action); try { switch (action) { case ACTION_LOCKED_BOOT_COMPLETED: handleLockedBootCompleted(context); break; case ACTION_BOOT_COMPLETED: handleBootCompleted(context); break; case ACTION_MY_PACKAGE_REPLACED: handlePackageReplaced(context, intent); break; default: Log.w(TAG, "Unknown action: " + action); break; } } catch (Exception e) { Log.e(TAG, "Error handling broadcast: " + action, e); } } private void handleLockedBootCompleted(Context context) { Log.i(TAG, "Locked boot completed - preparing for recovery"); try { Context deviceProtectedContext = context; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { deviceProtectedContext = context.createDeviceProtectedStorageContext(); } Log.i(TAG, "Locked boot completed - ready for full recovery on unlock"); } catch (Exception e) { Log.e(TAG, "Error during locked boot completion", e); } } private void handleBootCompleted(Context context) { Log.i(TAG, "Device boot completed - restoring notifications"); try { // Load all saved notifications DailyNotificationStorage storage = new DailyNotificationStorage(context); List notifications = storage.loadAllNotifications(); Log.d(TAG, "Found " + notifications.size() + " notifications to recover"); if (notifications.isEmpty()) { Log.d(TAG, "No notifications to recover"); return; } // Reschedule all notifications DailyNotificationScheduler scheduler = new DailyNotificationScheduler(context); int recoveredCount = 0; for (NotificationContent notification : notifications) { try { scheduler.scheduleNotification(notification); recoveredCount++; Log.d(TAG, "Recovered notification: " + notification.getId()); } catch (Exception e) { Log.w(TAG, "Failed to recover notification: " + notification.getId(), e); } } Log.i(TAG, "Notification recovery completed: " + recoveredCount + "/" + notifications.size() + " recovered"); } catch (Exception e) { Log.e(TAG, "Error during boot recovery", e); } } private void handlePackageReplaced(Context context, Intent intent) { Log.i(TAG, "Package replaced - restoring notifications"); // Use the same logic as boot completed handleBootCompleted(context); } } ``` ## ๐Ÿ“Š Data Model ### **NotificationContent.java - Core Data Structure** ```java @Entity(tableName = "notifications") public class NotificationContent { @PrimaryKey private String id; private String title; private String body; private long fetchedAt; // Immutable fetch timestamp private long scheduledAt; // Mutable schedule timestamp private String mediaUrl; private boolean sound; private String priority; private String url; // Transient field for Gson compatibility @Expose(serialize = false, deserialize = false) private transient long fetchTime; public NotificationContent(String id, String title, String body, long fetchedAt, boolean sound, String priority, String url) { this.id = id; this.title = title; this.body = body; this.fetchedAt = fetchedAt; this.scheduledAt = fetchedAt; // Initialize with fetch time this.sound = sound; this.priority = priority; this.url = url; this.fetchTime = fetchedAt; // Set transient field } // Custom JsonDeserializer for Gson public static class NotificationContentDeserializer implements JsonDeserializer { @Override public NotificationContent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); String id = jsonObject.get("id").getAsString(); String title = jsonObject.get("title").getAsString(); String body = jsonObject.get("body").getAsString(); long fetchedAt = jsonObject.get("fetchedAt").getAsLong(); boolean sound = jsonObject.get("sound").getAsBoolean(); String priority = jsonObject.get("priority").getAsString(); String url = jsonObject.get("url").getAsString(); // Create with constructor to ensure fetchedAt is set NotificationContent content = new NotificationContent(id, title, body, fetchedAt, sound, priority, url); // Set other fields if present if (jsonObject.has("scheduledAt")) { content.setScheduledAt(jsonObject.get("scheduledAt").getAsLong()); } if (jsonObject.has("mediaUrl")) { content.setMediaUrl(jsonObject.get("mediaUrl").getAsString()); } return content; } } // Getters and setters... public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public long getFetchedAt() { return fetchedAt; } public void setFetchedAt(long fetchedAt) { this.fetchedAt = fetchedAt; } public long getScheduledAt() { return scheduledAt; } public void setScheduledAt(long scheduledAt) { this.scheduledAt = scheduledAt; } public String getMediaUrl() { return mediaUrl; } public void setMediaUrl(String mediaUrl) { this.mediaUrl = mediaUrl; } public boolean isSound() { return sound; } public void setSound(boolean sound) { this.sound = sound; } public String getPriority() { return priority; } public void setPriority(String priority) { this.priority = priority; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public long getFetchTime() { return fetchTime; } public void setFetchTime(long fetchTime) { this.fetchTime = fetchTime; } } ``` ## ๐Ÿ—„๏ธ Storage Implementation ### **DailyNotificationStorage.java - Key Methods** ```java @Database(entities = {NotificationContent.class}, version = 1) public abstract class DailyNotificationStorage extends RoomDatabase { public abstract NotificationContentDao notificationContentDao(); private static DailyNotificationStorage INSTANCE; private static final String DATABASE_NAME = "daily_notifications"; public static DailyNotificationStorage getInstance(Context context) { if (INSTANCE == null) { synchronized (DailyNotificationStorage.class) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DailyNotificationStorage.class, DATABASE_NAME) .build(); } } } return INSTANCE; } public void saveNotificationContent(NotificationContent content) { try { notificationContentDao().insert(content); Log.d(TAG, "Saved notification: " + content.getId()); } catch (Exception e) { Log.e(TAG, "Error saving notification", e); throw e; } } public List loadAllNotifications() { try { List notifications = notificationContentDao().getAllNotifications(); Log.d(TAG, "Loaded " + notifications.size() + " notifications"); return notifications; } catch (Exception e) { Log.e(TAG, "Error loading notifications", e); return new ArrayList<>(); } } public void deleteNotification(String id) { try { notificationContentDao().deleteById(id); Log.d(TAG, "Deleted notification: " + id); } catch (Exception e) { Log.e(TAG, "Error deleting notification", e); } } } ``` ## ๐Ÿ”” Notification Scheduling ### **DailyNotificationScheduler.java - Core Scheduling** ```java public class DailyNotificationScheduler { private static final String TAG = "DailyNotificationScheduler"; private final Context context; private final AlarmManager alarmManager; public DailyNotificationScheduler(Context context) { this.context = context; this.alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); } public void scheduleNotification(NotificationContent content, int hour, int minute) { try { // Create intent for notification Intent intent = new Intent(context, DailyNotificationReceiver.class); intent.putExtra("notification_id", content.getId()); intent.putExtra("title", content.getTitle()); intent.putExtra("body", content.getBody()); intent.putExtra("sound", content.isSound()); intent.putExtra("priority", content.getPriority()); intent.putExtra("url", content.getUrl()); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, content.getId().hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // Calculate trigger time Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, minute); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); // If time has passed today, schedule for tomorrow if (calendar.getTimeInMillis() <= System.currentTimeMillis()) { calendar.add(Calendar.DAY_OF_MONTH, 1); } // Schedule exact alarm if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent ); } else { alarmManager.setExact( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent ); } Log.d(TAG, "Scheduled notification for " + hour + ":" + minute + " (ID: " + content.getId() + ")"); } catch (Exception e) { Log.e(TAG, "Error scheduling notification", e); throw e; } } public boolean isNotificationScheduled(String notificationId) { Intent intent = new Intent(context, DailyNotificationReceiver.class); intent.putExtra("notification_id", notificationId); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, notificationId.hashCode(), intent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE ); return pendingIntent != null; } } ``` ## ๐Ÿ“ฑ Android Manifest Configuration ### **AndroidManifest.xml - Key Sections** ```xml ``` ## ๐Ÿงช Test App JavaScript ### **Test App - Core Functions** ```javascript function testPlugin() { const status = document.getElementById('status'); status.innerHTML = 'Testing plugin...'; status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background try { if (!window.DailyNotification) { status.innerHTML = 'DailyNotification plugin not available'; status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background return; } // Plugin is loaded and ready status.innerHTML = 'Plugin is loaded and ready!'; status.style.background = 'rgba(0, 255, 0, 0.3)'; // Green background } catch (error) { status.innerHTML = `Plugin test failed: ${error.message}`; status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background } } function testNotification() { const status = document.getElementById('status'); status.innerHTML = 'Testing notification...'; status.style.background = 'rgba(255, 255, 0, 0.3)'; // Yellow background try { if (!window.DailyNotification) { status.innerHTML = 'DailyNotification plugin not available'; status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background return; } // Test the notification method directly console.log('Testing notification scheduling...'); const now = new Date(); const testTime = new Date(now.getTime() + 300000); // 5 minutes from now const timeString = testTime.getHours().toString().padStart(2, '0') + ':' + testTime.getMinutes().toString().padStart(2, '0'); window.DailyNotification.scheduleDailyNotification({ time: timeString, title: 'Test Notification', body: 'This is a test notification from the DailyNotification plugin!', sound: true, priority: 'high' }) .then(() => { status.innerHTML = 'Notification scheduled for ' + timeString + '! Check your notification bar in 5 minutes.'; status.style.background = 'rgba(0, 255, 0, 0.3)'; // Green background }) .catch(error => { status.innerHTML = `Notification failed: ${error.message}`; status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background }); } catch (error) { status.innerHTML = `Notification test failed: ${error.message}`; status.style.background = 'rgba(255, 0, 0, 0.3)'; // Red background } } ``` --- **These code snippets provide ChatGPT with the essential implementation details for comprehensive analysis and improvement recommendations.**