/** * DailyNotificationReceiver.java * * Broadcast receiver for handling scheduled notification alarms * Displays notifications when scheduled time is reached * * @author Matthew Raymer * @version 1.0.0 */ package com.timesafari.dailynotification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build; import android.util.Log; import androidx.core.app.NotificationCompat; /** * Broadcast receiver for daily notification alarms * * This receiver is triggered by AlarmManager when it's time to display * a notification. It retrieves the notification content from storage * and displays it to the user. */ public class DailyNotificationReceiver extends BroadcastReceiver { private static final String TAG = "DailyNotificationReceiver"; private static final String CHANNEL_ID = "timesafari.daily"; private static final String EXTRA_NOTIFICATION_ID = "notification_id"; /** * Handle broadcast intent when alarm triggers * * @param context Application context * @param intent Broadcast intent */ @Override public void onReceive(Context context, Intent intent) { try { Log.d(TAG, "Received notification broadcast"); String action = intent.getAction(); if (action == null) { Log.w(TAG, "Received intent with null action"); return; } if ("com.timesafari.daily.NOTIFICATION".equals(action)) { handleNotificationIntent(context, intent); } else { Log.w(TAG, "Unknown action: " + action); } } catch (Exception e) { Log.e(TAG, "Error handling broadcast", e); } } /** * Handle notification intent * * @param context Application context * @param intent Intent containing notification data */ private void handleNotificationIntent(Context context, Intent intent) { try { String notificationId = intent.getStringExtra(EXTRA_NOTIFICATION_ID); if (notificationId == null) { Log.w(TAG, "Notification ID not found in intent"); return; } Log.d(TAG, "Processing notification: " + notificationId); // Get notification content from storage DailyNotificationStorage storage = new DailyNotificationStorage(context); NotificationContent content = storage.getNotificationContent(notificationId); if (content == null) { Log.w(TAG, "Notification content not found: " + notificationId); return; } // Check if notification is ready to display if (!content.isReadyToDisplay()) { Log.d(TAG, "Notification not ready to display yet: " + notificationId); return; } // Display the notification displayNotification(context, content); // Schedule next notification if this is a recurring daily notification scheduleNextNotification(context, content); Log.i(TAG, "Notification processed successfully: " + notificationId); } catch (Exception e) { Log.e(TAG, "Error handling notification intent", e); } } /** * Display the notification to the user * * @param context Application context * @param content Notification content to display */ private void displayNotification(Context context, NotificationContent content) { try { Log.d(TAG, "Displaying notification: " + content.getId()); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { Log.e(TAG, "NotificationManager not available"); return; } // Create notification builder NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(android.R.drawable.ic_dialog_info) .setContentTitle(content.getTitle()) .setContentText(content.getBody()) .setPriority(getNotificationPriority(content.getPriority())) .setAutoCancel(true) .setCategory(NotificationCompat.CATEGORY_REMINDER); // Add sound if enabled if (content.isSound()) { builder.setDefaults(NotificationCompat.DEFAULT_SOUND); } // Add click action if URL is available if (content.getUrl() != null && !content.getUrl().isEmpty()) { Intent clickIntent = new Intent(Intent.ACTION_VIEW); clickIntent.setData(android.net.Uri.parse(content.getUrl())); PendingIntent clickPendingIntent = PendingIntent.getActivity( context, content.getId().hashCode(), clickIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); builder.setContentIntent(clickPendingIntent); } // Add dismiss action Intent dismissIntent = new Intent(context, DailyNotificationReceiver.class); dismissIntent.setAction("com.timesafari.daily.DISMISS"); dismissIntent.putExtra(EXTRA_NOTIFICATION_ID, content.getId()); PendingIntent dismissPendingIntent = PendingIntent.getBroadcast( context, content.getId().hashCode() + 1000, // Different request code dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); builder.addAction( android.R.drawable.ic_menu_close_clear_cancel, "Dismiss", dismissPendingIntent ); // Build and display notification int notificationId = content.getId().hashCode(); notificationManager.notify(notificationId, builder.build()); Log.i(TAG, "Notification displayed successfully: " + content.getId()); } catch (Exception e) { Log.e(TAG, "Error displaying notification", e); } } /** * Schedule the next occurrence of this daily notification * * @param context Application context * @param content Current notification content */ private void scheduleNextNotification(Context context, NotificationContent content) { try { Log.d(TAG, "Scheduling next notification for: " + content.getId()); // Calculate next occurrence (24 hours from now) long nextScheduledTime = content.getScheduledTime() + (24 * 60 * 60 * 1000); // Create new content for next occurrence NotificationContent nextContent = new NotificationContent(); nextContent.setTitle(content.getTitle()); nextContent.setBody(content.getBody()); nextContent.setScheduledTime(nextScheduledTime); nextContent.setSound(content.isSound()); nextContent.setPriority(content.getPriority()); nextContent.setUrl(content.getUrl()); nextContent.setFetchTime(System.currentTimeMillis()); // Save to storage DailyNotificationStorage storage = new DailyNotificationStorage(context); storage.saveNotificationContent(nextContent); // Schedule the notification DailyNotificationScheduler scheduler = new DailyNotificationScheduler( context, (android.app.AlarmManager) context.getSystemService(Context.ALARM_SERVICE) ); boolean scheduled = scheduler.scheduleNotification(nextContent); if (scheduled) { Log.i(TAG, "Next notification scheduled successfully"); } else { Log.e(TAG, "Failed to schedule next notification"); } } catch (Exception e) { Log.e(TAG, "Error scheduling next notification", e); } } /** * Get notification priority constant * * @param priority Priority string from content * @return NotificationCompat priority constant */ private int getNotificationPriority(String priority) { if (priority == null) { return NotificationCompat.PRIORITY_DEFAULT; } switch (priority.toLowerCase()) { case "high": return NotificationCompat.PRIORITY_HIGH; case "low": return NotificationCompat.PRIORITY_LOW; case "min": return NotificationCompat.PRIORITY_MIN; case "max": return NotificationCompat.PRIORITY_MAX; default: return NotificationCompat.PRIORITY_DEFAULT; } } /** * Handle notification dismissal * * @param context Application context * @param notificationId ID of dismissed notification */ private void handleNotificationDismissal(Context context, String notificationId) { try { Log.d(TAG, "Handling notification dismissal: " + notificationId); // Remove from storage DailyNotificationStorage storage = new DailyNotificationStorage(context); storage.removeNotification(notificationId); // Cancel any pending alarms DailyNotificationScheduler scheduler = new DailyNotificationScheduler( context, (android.app.AlarmManager) context.getSystemService(Context.ALARM_SERVICE) ); scheduler.cancelNotification(notificationId); Log.i(TAG, "Notification dismissed successfully: " + notificationId); } catch (Exception e) { Log.e(TAG, "Error handling notification dismissal", e); } } }