@@ -20,13 +20,42 @@ data class Note(
2020 val checklistItems : List <ChecklistItem >? = null
2121) {
2222 /* *
23- * Serialisiert Note zu JSON (v1.4.0: Nutzt Gson für komplexe Strukturen)
23+ * Serialisiert Note zu JSON
24+ * v1.4.0: Nutzt Gson für komplexe Strukturen
25+ * v1.4.1: Für Checklisten wird ein Fallback-Content generiert, damit ältere
26+ * App-Versionen (v1.3.x) die Notiz als Text anzeigen können.
2427 */
2528 fun toJson (): String {
2629 val gson = com.google.gson.GsonBuilder ()
2730 .setPrettyPrinting()
2831 .create()
29- return gson.toJson(this )
32+
33+ // v1.4.1: Für Checklisten den Fallback-Content generieren
34+ val noteToSerialize = if (noteType == NoteType .CHECKLIST && checklistItems != null ) {
35+ this .copy(content = generateChecklistFallbackContent())
36+ } else {
37+ this
38+ }
39+
40+ return gson.toJson(noteToSerialize)
41+ }
42+
43+ /* *
44+ * v1.4.1: Generiert einen lesbaren Text-Fallback aus Checklist-Items.
45+ * Format: GitHub-Style Task-Listen (kompatibel mit Markdown)
46+ *
47+ * Beispiel:
48+ * [ ] Milch kaufen
49+ * [x] Brot gekauft
50+ * [ ] Eier
51+ *
52+ * Wird von älteren App-Versionen (v1.3.x) als normaler Text angezeigt.
53+ */
54+ private fun generateChecklistFallbackContent (): String {
55+ return checklistItems?.sortedBy { it.order }?.joinToString(" \n " ) { item ->
56+ val checkbox = if (item.isChecked) " [x]" else " [ ]"
57+ " $checkbox ${item.text} "
58+ } ? : " "
3059 }
3160
3261 /* *
@@ -88,7 +117,7 @@ type: ${noteType.name.lowercase()}
88117
89118 // Checklist-Items parsen (kann null sein)
90119 val checklistItemsType = object : com.google.gson.reflect.TypeToken <List <ChecklistItem >>() {}.type
91- val checklistItems = if (jsonObject.has(" checklistItems" ) &&
120+ var checklistItems: List < ChecklistItem > ? = if (jsonObject.has(" checklistItems" ) &&
92121 ! jsonObject.get(" checklistItems" ).isJsonNull
93122 ) {
94123 gson.fromJson<List <ChecklistItem >>(
@@ -99,6 +128,19 @@ type: ${noteType.name.lowercase()}
99128 null
100129 }
101130
131+ // v1.4.1: Recovery-Mode - Falls Checkliste aber keine Items,
132+ // versuche Content als Fallback zu parsen
133+ if (noteType == NoteType .CHECKLIST &&
134+ (checklistItems == null || checklistItems.isEmpty()) &&
135+ rawNote.content.isNotBlank()) {
136+
137+ val recoveredItems = parseChecklistFromContent(rawNote.content)
138+ if (recoveredItems.isNotEmpty()) {
139+ Logger .d(TAG , " 🔄 Recovered ${recoveredItems.size} checklist items from content fallback" )
140+ checklistItems = recoveredItems
141+ }
142+ }
143+
102144 // Note mit korrekten Werten erstellen
103145 Note (
104146 id = rawNote.id,
@@ -130,6 +172,34 @@ type: ${noteType.name.lowercase()}
130172 val syncStatus : SyncStatus ? = null
131173 )
132174
175+ /* *
176+ * v1.4.1: Parst GitHub-Style Checklisten aus Text (Recovery-Mode).
177+ *
178+ * Unterstützte Formate:
179+ * - [ ] Unchecked item
180+ * - [x] Checked item
181+ * - [X] Checked item (case insensitive)
182+ *
183+ * Wird verwendet, wenn eine v1.4.0 Checkliste von einer älteren
184+ * App-Version (v1.3.x) bearbeitet wurde und die checklistItems verloren gingen.
185+ *
186+ * @param content Der Text-Content der Notiz
187+ * @return Liste von ChecklistItems oder leere Liste
188+ */
189+ private fun parseChecklistFromContent (content : String ): List <ChecklistItem > {
190+ val pattern = Regex (""" ^\s*\[([ xX])\]\s*(.+)$""" , RegexOption .MULTILINE )
191+ return pattern.findAll(content).mapIndexed { index, match ->
192+ val checked = match.groupValues[1 ].lowercase() == " x"
193+ val text = match.groupValues[2 ].trim()
194+ ChecklistItem (
195+ id = UUID .randomUUID().toString(),
196+ text = text,
197+ isChecked = checked,
198+ order = index
199+ )
200+ }.toList()
201+ }
202+
133203 /* *
134204 * Parst Markdown zurück zu Note-Objekt (Task #1.2.0-09)
135205 * v1.4.0: Unterstützt jetzt auch Checklisten-Format
0 commit comments