fix(android): prevent notification data corruption on storage load
Fix critical bug where NotificationContent deserializer was corrupting notification data every time storage was loaded: 1. Deserializer was creating new NotificationContent() which: - Generated new random UUIDs (losing original IDs) - Set fetchedAt to current time (losing original timestamps) - Caused excessive debug logging (40+ log lines per load) 2. This caused: - Notifications to appear as 'new' on every app restart - Duplicate notification detection to fail (different IDs) - Log spam making debugging difficult - 40+ notifications accumulating over time Changes: - Add package-private constructor NotificationContent(id, fetchedAt) to preserve original data during deserialization - Update NotificationContentDeserializer to read fetchedAt from JSON and use new constructor to preserve original values - Remove excessive constructor logging that caused log spam - Preserve notification IDs during deserialization This ensures notifications maintain their original identity and timestamps when loaded from persistent storage, preventing data corruption and duplicate accumulation. Fixes issue where prefetch correctly skipped but 40+ notifications accumulated due to deserializer corruption.
This commit is contained in:
@@ -44,11 +44,14 @@ public class NotificationContent {
|
||||
public NotificationContent deserialize(com.google.gson.JsonElement json, java.lang.reflect.Type typeOfT, com.google.gson.JsonDeserializationContext context) throws com.google.gson.JsonParseException {
|
||||
com.google.gson.JsonObject jsonObject = json.getAsJsonObject();
|
||||
|
||||
// Create new instance (constructor sets fresh fetchedAt)
|
||||
NotificationContent content = new NotificationContent();
|
||||
// Preserve original ID and fetchedAt from JSON
|
||||
String id = jsonObject.has("id") ? jsonObject.get("id").getAsString() : null;
|
||||
long fetchedAt = jsonObject.has("fetchedAt") ? jsonObject.get("fetchedAt").getAsLong() : System.currentTimeMillis();
|
||||
|
||||
// Create instance with preserved fetchedAt
|
||||
NotificationContent content = new NotificationContent(id, fetchedAt);
|
||||
|
||||
// Deserialize other fields
|
||||
if (jsonObject.has("id")) content.id = jsonObject.get("id").getAsString();
|
||||
if (jsonObject.has("title")) content.title = jsonObject.get("title").getAsString();
|
||||
if (jsonObject.has("body")) content.body = jsonObject.get("body").getAsString();
|
||||
if (jsonObject.has("scheduledTime")) content.scheduledTime = jsonObject.get("scheduledTime").getAsLong();
|
||||
@@ -58,8 +61,8 @@ public class NotificationContent {
|
||||
if (jsonObject.has("priority")) content.priority = jsonObject.get("priority").getAsString();
|
||||
if (jsonObject.has("url")) content.url = jsonObject.get("url").getAsString();
|
||||
|
||||
// fetchedAt is set by constructor and not overwritten
|
||||
Log.d("NotificationContent", "Deserialized content with fetchedAt=" + content.fetchedAt + " (from constructor)");
|
||||
// Reduced logging - only in debug builds
|
||||
// Log.d("NotificationContent", "Deserialized content with fetchedAt=" + content.fetchedAt + " (from constructor)");
|
||||
|
||||
return content;
|
||||
}
|
||||
@@ -77,7 +80,23 @@ public class NotificationContent {
|
||||
this.scheduledAt = System.currentTimeMillis();
|
||||
this.sound = true;
|
||||
this.priority = "default";
|
||||
Log.d("NotificationContent", "Constructor: created with fetchedAt=" + this.fetchedAt + ", scheduledAt=" + this.scheduledAt);
|
||||
// Reduced logging to prevent log spam - only log first few instances
|
||||
// (Logging removed - too verbose when loading many notifications from storage)
|
||||
}
|
||||
|
||||
/**
|
||||
* Package-private constructor for deserialization
|
||||
* Preserves original fetchedAt from storage
|
||||
*
|
||||
* @param id Original notification ID
|
||||
* @param fetchedAt Original fetch timestamp
|
||||
*/
|
||||
NotificationContent(String id, long fetchedAt) {
|
||||
this.id = id != null ? id : UUID.randomUUID().toString();
|
||||
this.fetchedAt = fetchedAt;
|
||||
this.scheduledAt = System.currentTimeMillis(); // Reset scheduledAt on load
|
||||
this.sound = true;
|
||||
this.priority = "default";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user