/** * NotificationContent.java * * Data model for notification content following the project directive schema * Implements the canonical NotificationContent v1 structure * * @author Matthew Raymer * @version 1.0.0 */ package com.timesafari.dailynotification; import java.util.UUID; /** * Represents notification content with all required fields * * This class follows the canonical schema defined in the project directive: * - id: string (uuid) * - title: string * - body: string (plain text; may include simple emoji) * - scheduledTime: epoch millis (client-local target) * - mediaUrl: string? (for future; must be mirrored to local path before use) * - fetchTime: epoch millis */ public class NotificationContent { private String id; private String title; private String body; private long scheduledTime; private String mediaUrl; private long fetchTime; private boolean sound; private String priority; private String url; /** * Default constructor with auto-generated UUID */ public NotificationContent() { this.id = UUID.randomUUID().toString(); this.fetchTime = System.currentTimeMillis(); this.sound = true; this.priority = "default"; } /** * Constructor with all required fields * * @param title Notification title * @param body Notification body text * @param scheduledTime When to display the notification */ public NotificationContent(String title, String body, long scheduledTime) { this(); this.title = title; this.body = body; this.scheduledTime = scheduledTime; } // Getters and Setters /** * Get the unique identifier for this notification * * @return UUID string */ public String getId() { return id; } /** * Set the unique identifier for this notification * * @param id UUID string */ public void setId(String id) { this.id = id; } /** * Get the notification title * * @return Title string */ public String getTitle() { return title; } /** * Set the notification title * * @param title Title string */ public void setTitle(String title) { this.title = title; } /** * Get the notification body text * * @return Body text string */ public String getBody() { return body; } /** * Set the notification body text * * @param body Body text string */ public void setBody(String body) { this.body = body; } /** * Get the scheduled time for this notification * * @return Timestamp in milliseconds */ public long getScheduledTime() { return scheduledTime; } /** * Set the scheduled time for this notification * * @param scheduledTime Timestamp in milliseconds */ public void setScheduledTime(long scheduledTime) { this.scheduledTime = scheduledTime; } /** * Get the media URL (optional, for future use) * * @return Media URL string or null */ public String getMediaUrl() { return mediaUrl; } /** * Set the media URL (optional, for future use) * * @param mediaUrl Media URL string or null */ public void setMediaUrl(String mediaUrl) { this.mediaUrl = mediaUrl; } /** * Get the fetch time when content was retrieved * * @return Timestamp in milliseconds */ public long getFetchTime() { return fetchTime; } /** * Set the fetch time when content was retrieved * * @param fetchTime Timestamp in milliseconds */ public void setFetchTime(long fetchTime) { this.fetchTime = fetchTime; } /** * Check if sound should be played * * @return true if sound is enabled */ public boolean isSound() { return sound; } /** * Set whether sound should be played * * @param sound true to enable sound */ public void setSound(boolean sound) { this.sound = sound; } /** * Get the notification priority * * @return Priority string (high, default, low) */ public String getPriority() { return priority; } /** * Set the notification priority * * @param priority Priority string (high, default, low) */ public void setPriority(String priority) { this.priority = priority; } /** * Get the associated URL * * @return URL string or null */ public String getUrl() { return url; } /** * Set the associated URL * * @param url URL string or null */ public void setUrl(String url) { this.url = url; } /** * Check if this notification is stale (older than 24 hours) * * @return true if notification is stale */ public boolean isStale() { long currentTime = System.currentTimeMillis(); long age = currentTime - fetchTime; return age > 24 * 60 * 60 * 1000; // 24 hours in milliseconds } /** * Get the age of this notification in milliseconds * * @return Age in milliseconds */ public long getAge() { return System.currentTimeMillis() - fetchTime; } /** * Get the age of this notification in a human-readable format * * @return Human-readable age string */ public String getAgeString() { long age = getAge(); long seconds = age / 1000; long minutes = seconds / 60; long hours = minutes / 60; long days = hours / 24; if (days > 0) { return days + " day" + (days == 1 ? "" : "s") + " ago"; } else if (hours > 0) { return hours + " hour" + (hours == 1 ? "" : "s") + " ago"; } else if (minutes > 0) { return minutes + " minute" + (minutes == 1 ? "" : "s") + " ago"; } else { return "just now"; } } /** * Check if this notification is ready to be displayed * * @return true if notification should be displayed now */ public boolean isReadyToDisplay() { return System.currentTimeMillis() >= scheduledTime; } /** * Get time until this notification should be displayed * * @return Time in milliseconds until display */ public long getTimeUntilDisplay() { return Math.max(0, scheduledTime - System.currentTimeMillis()); } @Override public String toString() { return "NotificationContent{" + "id='" + id + '\'' + ", title='" + title + '\'' + ", body='" + body + '\'' + ", scheduledTime=" + scheduledTime + ", mediaUrl='" + mediaUrl + '\'' + ", fetchTime=" + fetchTime + ", sound=" + sound + ", priority='" + priority + '\'' + ", url='" + url + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotificationContent that = (NotificationContent) o; return id.equals(that.id); } @Override public int hashCode() { return id.hashCode(); } }