docs: add comprehensive static daily reminders documentation

- Add static daily reminders to README.md core features and API reference
- Create detailed usage guide in USAGE.md with examples and best practices
- Add version 2.1.0 changelog entry documenting new reminder functionality
- Create examples/static-daily-reminders.ts with complete usage examples
- Update test-apps README to include reminder testing capabilities

The static daily reminder feature provides simple daily notifications
without network content dependency, supporting cross-platform scheduling
with rich customization options.
This commit is contained in:
Matthew Raymer
2025-10-05 05:12:06 +00:00
parent 9ec30974da
commit f9c21d4e5b
21 changed files with 2120 additions and 0 deletions

View File

@@ -1583,4 +1583,353 @@ public class DailyNotificationPlugin extends Plugin {
call.reject("Coordination status retrieval failed: " + e.getMessage());
}
}
// Static Daily Reminder Methods
@PluginMethod
public void scheduleDailyReminder(PluginCall call) {
try {
Log.d(TAG, "Scheduling daily reminder");
// Extract reminder options
String id = call.getString("id");
String title = call.getString("title");
String body = call.getString("body");
String time = call.getString("time");
boolean sound = call.getBoolean("sound", true);
boolean vibration = call.getBoolean("vibration", true);
String priority = call.getString("priority", "normal");
boolean repeatDaily = call.getBoolean("repeatDaily", true);
String timezone = call.getString("timezone");
// Validate required parameters
if (id == null || title == null || body == null || time == null) {
call.reject("Missing required parameters: id, title, body, time");
return;
}
// Parse time (HH:mm format)
String[] timeParts = time.split(":");
if (timeParts.length != 2) {
call.reject("Invalid time format. Use HH:mm (e.g., 09:00)");
return;
}
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
call.reject("Invalid time values. Hour must be 0-23, minute must be 0-59");
return;
}
// Create reminder content
NotificationContent reminderContent = new NotificationContent();
reminderContent.setId("reminder_" + id); // Prefix to identify as reminder
reminderContent.setTitle(title);
reminderContent.setBody(body);
reminderContent.setSound(sound);
reminderContent.setPriority(priority);
reminderContent.setFetchTime(System.currentTimeMillis());
// Calculate next 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);
}
reminderContent.setScheduledTime(calendar.getTimeInMillis());
// Store reminder in database
storeReminderInDatabase(id, title, body, time, sound, vibration, priority, repeatDaily, timezone);
// Schedule the notification
boolean scheduled = scheduler.scheduleNotification(reminderContent);
if (scheduled) {
Log.i(TAG, "Daily reminder scheduled successfully: " + id);
call.resolve();
} else {
call.reject("Failed to schedule daily reminder");
}
} catch (Exception e) {
Log.e(TAG, "Error scheduling daily reminder", e);
call.reject("Daily reminder scheduling failed: " + e.getMessage());
}
}
@PluginMethod
public void cancelDailyReminder(PluginCall call) {
try {
Log.d(TAG, "Cancelling daily reminder");
String reminderId = call.getString("reminderId");
if (reminderId == null) {
call.reject("Missing reminderId parameter");
return;
}
// Cancel the scheduled notification (use prefixed ID)
scheduler.cancelNotification("reminder_" + reminderId);
// Remove from database
removeReminderFromDatabase(reminderId);
Log.i(TAG, "Daily reminder cancelled: " + reminderId);
call.resolve();
} catch (Exception e) {
Log.e(TAG, "Error cancelling daily reminder", e);
call.reject("Daily reminder cancellation failed: " + e.getMessage());
}
}
@PluginMethod
public void getScheduledReminders(PluginCall call) {
try {
Log.d(TAG, "Getting scheduled reminders");
// Get reminders from database
java.util.List<DailyReminderInfo> reminders = getRemindersFromDatabase();
// Convert to JSObject array
JSObject result = new JSObject();
result.put("reminders", reminders);
call.resolve(result);
} catch (Exception e) {
Log.e(TAG, "Error getting scheduled reminders", e);
call.reject("Failed to get scheduled reminders: " + e.getMessage());
}
}
@PluginMethod
public void updateDailyReminder(PluginCall call) {
try {
Log.d(TAG, "Updating daily reminder");
String reminderId = call.getString("reminderId");
if (reminderId == null) {
call.reject("Missing reminderId parameter");
return;
}
// Extract updated options
String title = call.getString("title");
String body = call.getString("body");
String time = call.getString("time");
Boolean sound = call.getBoolean("sound");
Boolean vibration = call.getBoolean("vibration");
String priority = call.getString("priority");
Boolean repeatDaily = call.getBoolean("repeatDaily");
String timezone = call.getString("timezone");
// Cancel existing reminder (use prefixed ID)
scheduler.cancelNotification("reminder_" + reminderId);
// Update in database
updateReminderInDatabase(reminderId, title, body, time, sound, vibration, priority, repeatDaily, timezone);
// Reschedule with new settings
if (title != null && body != null && time != null) {
// Create new reminder content
NotificationContent reminderContent = new NotificationContent();
reminderContent.setId("reminder_" + reminderId); // Prefix to identify as reminder
reminderContent.setTitle(title);
reminderContent.setBody(body);
reminderContent.setSound(sound != null ? sound : true);
reminderContent.setPriority(priority != null ? priority : "normal");
reminderContent.setFetchTime(System.currentTimeMillis());
// Calculate next trigger time
String[] timeParts = time.split(":");
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
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 (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
reminderContent.setScheduledTime(calendar.getTimeInMillis());
// Schedule the updated notification
boolean scheduled = scheduler.scheduleNotification(reminderContent);
if (!scheduled) {
call.reject("Failed to reschedule updated reminder");
return;
}
}
Log.i(TAG, "Daily reminder updated: " + reminderId);
call.resolve();
} catch (Exception e) {
Log.e(TAG, "Error updating daily reminder", e);
call.reject("Daily reminder update failed: " + e.getMessage());
}
}
// Helper methods for reminder database operations
private void storeReminderInDatabase(String id, String title, String body, String time,
boolean sound, boolean vibration, String priority,
boolean repeatDaily, String timezone) {
try {
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(id + "_title", title);
editor.putString(id + "_body", body);
editor.putString(id + "_time", time);
editor.putBoolean(id + "_sound", sound);
editor.putBoolean(id + "_vibration", vibration);
editor.putString(id + "_priority", priority);
editor.putBoolean(id + "_repeatDaily", repeatDaily);
editor.putString(id + "_timezone", timezone);
editor.putLong(id + "_createdAt", System.currentTimeMillis());
editor.putBoolean(id + "_isScheduled", true);
editor.apply();
Log.d(TAG, "Reminder stored in database: " + id);
} catch (Exception e) {
Log.e(TAG, "Error storing reminder in database", e);
}
}
private void removeReminderFromDatabase(String id) {
try {
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.remove(id + "_title");
editor.remove(id + "_body");
editor.remove(id + "_time");
editor.remove(id + "_sound");
editor.remove(id + "_vibration");
editor.remove(id + "_priority");
editor.remove(id + "_repeatDaily");
editor.remove(id + "_timezone");
editor.remove(id + "_createdAt");
editor.remove(id + "_isScheduled");
editor.remove(id + "_lastTriggered");
editor.apply();
Log.d(TAG, "Reminder removed from database: " + id);
} catch (Exception e) {
Log.e(TAG, "Error removing reminder from database", e);
}
}
private java.util.List<DailyReminderInfo> getRemindersFromDatabase() {
java.util.List<DailyReminderInfo> reminders = new java.util.ArrayList<>();
try {
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
java.util.Map<String, ?> allEntries = prefs.getAll();
java.util.Set<String> reminderIds = new java.util.HashSet<>();
for (String key : allEntries.keySet()) {
if (key.endsWith("_title")) {
String id = key.substring(0, key.length() - 6); // Remove "_title"
reminderIds.add(id);
}
}
for (String id : reminderIds) {
DailyReminderInfo reminder = new DailyReminderInfo();
reminder.id = id;
reminder.title = prefs.getString(id + "_title", "");
reminder.body = prefs.getString(id + "_body", "");
reminder.time = prefs.getString(id + "_time", "");
reminder.sound = prefs.getBoolean(id + "_sound", true);
reminder.vibration = prefs.getBoolean(id + "_vibration", true);
reminder.priority = prefs.getString(id + "_priority", "normal");
reminder.repeatDaily = prefs.getBoolean(id + "_repeatDaily", true);
reminder.timezone = prefs.getString(id + "_timezone", null);
reminder.isScheduled = prefs.getBoolean(id + "_isScheduled", false);
reminder.createdAt = prefs.getLong(id + "_createdAt", 0);
reminder.lastTriggered = prefs.getLong(id + "_lastTriggered", 0);
// Calculate next trigger time
String[] timeParts = reminder.time.split(":");
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
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 (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
reminder.nextTriggerTime = calendar.getTimeInMillis();
reminders.add(reminder);
}
} catch (Exception e) {
Log.e(TAG, "Error getting reminders from database", e);
}
return reminders;
}
private void updateReminderInDatabase(String id, String title, String body, String time,
Boolean sound, Boolean vibration, String priority,
Boolean repeatDaily, String timezone) {
try {
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (title != null) editor.putString(id + "_title", title);
if (body != null) editor.putString(id + "_body", body);
if (time != null) editor.putString(id + "_time", time);
if (sound != null) editor.putBoolean(id + "_sound", sound);
if (vibration != null) editor.putBoolean(id + "_vibration", vibration);
if (priority != null) editor.putString(id + "_priority", priority);
if (repeatDaily != null) editor.putBoolean(id + "_repeatDaily", repeatDaily);
if (timezone != null) editor.putString(id + "_timezone", timezone);
editor.apply();
Log.d(TAG, "Reminder updated in database: " + id);
} catch (Exception e) {
Log.e(TAG, "Error updating reminder in database", e);
}
}
// Data class for reminder info
public static class DailyReminderInfo {
public String id;
public String title;
public String body;
public String time;
public boolean sound;
public boolean vibration;
public String priority;
public boolean repeatDaily;
public String timezone;
public boolean isScheduled;
public long nextTriggerTime;
public long createdAt;
public long lastTriggered;
}
}

View File

@@ -108,6 +108,17 @@ public class DailyNotificationScheduler {
intent.setAction(ACTION_NOTIFICATION);
intent.putExtra(EXTRA_NOTIFICATION_ID, content.getId());
// Check if this is a static reminder
if (content.getId().startsWith("reminder_") || content.getId().contains("_reminder")) {
intent.putExtra("is_static_reminder", true);
intent.putExtra("reminder_id", content.getId());
intent.putExtra("title", content.getTitle());
intent.putExtra("body", content.getBody());
intent.putExtra("sound", content.isSound());
intent.putExtra("vibration", true); // Default to true for reminders
intent.putExtra("priority", content.getPriority());
}
// Create pending intent with unique request code
int requestCode = content.getId().hashCode();
PendingIntent pendingIntent = PendingIntent.getBroadcast(