diff --git a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/NotePreviewWidget.java b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/NotePreviewWidget.java index 66c22778d..757187c67 100644 --- a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/NotePreviewWidget.java +++ b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/NotePreviewWidget.java @@ -8,10 +8,8 @@ import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.widget.RemoteViews; -import com.google.gson.Gson; import com.streetwriters.notesnook.datatypes.Note; import java.util.HashSet; @@ -22,18 +20,10 @@ public class NotePreviewWidget extends AppWidgetProvider { static String OpenNoteId = "com.streetwriters.notesnook.OpenNoteId"; static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { - String data = context.getSharedPreferences("appPreview", Context.MODE_PRIVATE).getString(String.valueOf(appWidgetId), ""); + String data = context.getSharedPreferences(WidgetUtils.PREFERENCES, Context.MODE_PRIVATE).getString(String.valueOf(appWidgetId), ""); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.note_widget); - Note note = null; - if (data != null && !data.isEmpty()) { - try { - note = new Gson().fromJson(data, Note.class); - } catch (Exception e) { - Log.e("NotePreviewWidget", "Could not read the note stored for widget " + appWidgetId, e); - } - } - + Note note = WidgetUtils.parseNote(data); if (note == null) { // Either the widget was never configured, or we lost the note it pointed at (ids // reassigned, data cleared). Point it back at the picker rather than leaving the user diff --git a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/RCTNNativeModule.java b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/RCTNNativeModule.java index e5872cfb5..cf21ecbdf 100644 --- a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/RCTNNativeModule.java +++ b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/RCTNNativeModule.java @@ -29,7 +29,6 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; -import com.google.gson.Gson; import com.streetwriters.notesnook.datatypes.Note; import java.util.ArrayList; @@ -156,14 +155,8 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule { @ReactMethod public void getWidgetNotes(Promise promise) { - SharedPreferences pref = getReactApplicationContext().getSharedPreferences("appPreview", Context.MODE_PRIVATE); - Map map = pref.getAll(); WritableArray arr = Arguments.createArray(); - for(Map.Entry entry : map.entrySet()){ - if (entry.getKey().equals("remindersList")) continue; - String value = (String) entry.getValue(); - Gson gson = new Gson(); - Note note = gson.fromJson(value, Note.class); + for (Note note : WidgetUtils.getWidgetNotes(getReactApplicationContext()).values()) { arr.pushString(note.getId()); } promise.resolve(arr); @@ -171,33 +164,33 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule { @ReactMethod public void hasWidgetNote(final String noteId, Promise promise) { - SharedPreferences pref = getReactApplicationContext().getSharedPreferences("appPreview", Context.MODE_PRIVATE); - Map map = pref.getAll(); boolean found = false; - for(Map.Entry entry : map.entrySet()){ - String value = (String) entry.getValue(); - if (value.contains(noteId)) { + for (Note note : WidgetUtils.getWidgetNotes(getReactApplicationContext()).values()) { + if (note.getId().equals(noteId)) { found = true; + break; } } promise.resolve(found); } + @ReactMethod public void updateWidgetNote(final String noteId, final String data) { - SharedPreferences pref = getReactApplicationContext().getSharedPreferences("appPreview", Context.MODE_PRIVATE); - Map map = pref.getAll(); + SharedPreferences pref = getReactApplicationContext().getSharedPreferences(WidgetUtils.PREFERENCES, Context.MODE_PRIVATE); SharedPreferences.Editor edit = pref.edit(); - ArrayList ids = new ArrayList<>(); - for(Map.Entry entry : map.entrySet()) { - String value = (String) entry.getValue(); - if (value.contains(noteId)) { - edit.putString(entry.getKey(), data); - ids.add(entry.getKey()); - } + List ids = new ArrayList<>(); + + // Match on the note's id, not on the raw JSON containing it somewhere: a note whose body + // happens to mention another note's id is not the same note. + for (Map.Entry entry : WidgetUtils.getWidgetNotes(getReactApplicationContext()).entrySet()) { + if (!noteId.equals(entry.getValue().getId())) continue; + edit.putString(String.valueOf(entry.getKey()), data); + ids.add(entry.getKey()); } edit.apply(); - for (String id: ids) { - NotePreviewWidget.updateAppWidget(mContext, AppWidgetManager.getInstance(mContext), Integer.parseInt(id)); + + for (int id : ids) { + NotePreviewWidget.updateAppWidget(mContext, AppWidgetManager.getInstance(mContext), id); } } diff --git a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/WidgetUtils.java b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/WidgetUtils.java index fa1d053a2..d3ab69a64 100644 --- a/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/WidgetUtils.java +++ b/apps/mobile/android/app/src/main/java/com/streetwriters/notesnook/WidgetUtils.java @@ -13,11 +13,14 @@ import android.widget.RemoteViews; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; +import com.streetwriters.notesnook.datatypes.Note; import com.streetwriters.notesnook.datatypes.Reminder; import java.util.ArrayList; import java.util.Calendar; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Shared helpers for the home screen widgets. @@ -33,6 +36,50 @@ public class WidgetUtils { */ private static final int MAX_REMINDERS = 50; + /** + * The note each note widget is showing, keyed by widget id. + * + * The preferences file mixes two things: one note per widget id, and the reminders list under + * its own key. Only numeric keys are widget notes, so anything else is skipped rather than + * being treated as a note. + */ + static Map getWidgetNotes(Context context) { + SharedPreferences preferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE); + Map notes = new LinkedHashMap<>(); + + for (Map.Entry entry : preferences.getAll().entrySet()) { + Integer widgetId = parseWidgetId(entry.getKey()); + if (widgetId == null) continue; + if (!(entry.getValue() instanceof String)) continue; + + Note note = parseNote((String) entry.getValue()); + if (note == null || note.getId() == null) continue; + notes.put(widgetId, note); + } + return notes; + } + + /** + * The widget id a preferences key refers to, or null if the key is not a widget id at all. + */ + private static Integer parseWidgetId(String key) { + try { + return Integer.valueOf(key); + } catch (NumberFormatException e) { + return null; + } + } + + static Note parseNote(String data) { + if (data == null || data.isEmpty()) return null; + try { + return new Gson().fromJson(data, Note.class); + } catch (Exception e) { + Log.e("NotePreviewWidget", "Could not read a stored note", e); + return null; + } + } + /** * The reminders the app last wrote out, minus any that have since fired. Reading and filtering * happens here so the provider can push the rows straight into the widget.